net/wireless/util.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
Fix an issue detected by syzbot with KMSAN
BUG: KMSAN: uninit-value in cfg80211_classify8021d+0x99d/0x12b0
net/wireless/util.c:1027
The function accessed DSCP fields from IP and IPv6 headers without first
verifying that sufficient header data was present in the skb. When a
packet reaches this path, the header dereference could access
uninitialized memory, as reported by KMSAN under fuzzing with syzkaller.
Add explicit skb_header_pointer() checks for both IPv4 and IPv6 headers to
ensure that the required header data is available before extracting the
DSCP field. This prevents uninitialized memory reads while preserving
existing behavior for valid packets
This fix has been tested and validated by syzbot. This patch closes the
bug reported at the following syzkaller link.Fixes the uninitialized
header access.
Reported-by: syzbot+878ddc3962f792e9af59@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com./bug?extid=878ddc3962f792e9af59
Tested-by: syzbot+878ddc3962f792e9af59@syzkaller.appspotmail.com
Fixes: b156579b1404 ("wireless: Treat IPv6 diffserv the same as IPv4 for 802.11e")
Signed-off-by: Ranganath V N <vnranganath.20@gmail.com>
---
validate header before DSCP read in cfg80211_classify8021d().
skb_header_pointer() checks before accessing header structures to ensure
safe and fully initialized data access.
Changes in v3:
- Corrected the code by using the correct offset past the header.
similar to the existing MPLs handling logic.
- Link to v2:https://lore.kernel.org/all/20251108-fifth-v2-1-405da01c6684@gmail.com
Changes in v2:
- Corrected the commit subject and Fixes tag.
- Link to v1: https://lore.kernel.org/r/20251103-fifth-v1-1-4a221737ddfe@gmail.com
---
net/wireless/util.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 56724b33af04..f2a6644d854e 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -963,9 +963,23 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,
switch (skb->protocol) {
case htons(ETH_P_IP):
+ struct iphdr iph, *ip;
+
+ ip = skb_header_pointer(skb, sizeof(struct ethhdr),
+ sizeof(*ip), &iph);
+ if (!ip)
+ return 0;
+
dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc;
break;
case htons(ETH_P_IPV6):
+ struct ipv6hdr ip6h, *ip6;
+
+ ip6 = skb_header_pointer(skb, sizeof(struct ethhdr),
+ sizeof(*ip6), &ip6h);
+ if (!ip6)
+ return 0;
+
dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & 0xfc;
break;
case htons(ETH_P_MPLS_UC):
--
2.43.0
Hi Ranganath,
kernel test robot noticed the following build errors:
[auto build test ERROR on wireless-next/main]
[also build test ERROR on wireless/main linus/master v6.18-rc6 next-20251119]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Ranganath-V-N/wifi-cfg80211-Fix-uninitialized-header-access-in-cfg80211_classify8021d/20251118-231316
base: https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git main
patch link: https://lore.kernel.org/r/20251118150524.7973-1-vnranganath.20%40gmail.com
patch subject: [PATCH v3] wifi: cfg80211: Fix uninitialized header access in cfg80211_classify8021d
config: arm64-randconfig-r132-20251119 (https://download.01.org/0day-ci/archive/20251119/202511192205.7ZRQgwZI-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251119/202511192205.7ZRQgwZI-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202511192205.7ZRQgwZI-lkp@intel.com/
All errors (new ones prefixed by >>):
net/wireless/util.c: In function 'cfg80211_classify8021d':
>> net/wireless/util.c:966:3: error: a label can only be part of a statement and a declaration is not a statement
struct iphdr iph, *ip;
^~~~~~
net/wireless/util.c:976:3: error: a label can only be part of a statement and a declaration is not a statement
struct ipv6hdr ip6h, *ip6;
^~~~~~
vim +966 net/wireless/util.c
936
937 /* Given a data frame determine the 802.1p/1d tag to use. */
938 unsigned int cfg80211_classify8021d(struct sk_buff *skb,
939 struct cfg80211_qos_map *qos_map)
940 {
941 unsigned int dscp;
942 unsigned char vlan_priority;
943 unsigned int ret;
944
945 /* skb->priority values from 256->263 are magic values to
946 * directly indicate a specific 802.1d priority. This is used
947 * to allow 802.1d priority to be passed directly in from VLAN
948 * tags, etc.
949 */
950 if (skb->priority >= 256 && skb->priority <= 263) {
951 ret = skb->priority - 256;
952 goto out;
953 }
954
955 if (skb_vlan_tag_present(skb)) {
956 vlan_priority = (skb_vlan_tag_get(skb) & VLAN_PRIO_MASK)
957 >> VLAN_PRIO_SHIFT;
958 if (vlan_priority > 0) {
959 ret = vlan_priority;
960 goto out;
961 }
962 }
963
964 switch (skb->protocol) {
965 case htons(ETH_P_IP):
> 966 struct iphdr iph, *ip;
967
968 ip = skb_header_pointer(skb, sizeof(struct ethhdr),
969 sizeof(*ip), &iph);
970 if (!ip)
971 return 0;
972
973 dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc;
974 break;
975 case htons(ETH_P_IPV6):
976 struct ipv6hdr ip6h, *ip6;
977
978 ip6 = skb_header_pointer(skb, sizeof(struct ethhdr),
979 sizeof(*ip6), &ip6h);
980 if (!ip6)
981 return 0;
982
983 dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & 0xfc;
984 break;
985 case htons(ETH_P_MPLS_UC):
986 case htons(ETH_P_MPLS_MC): {
987 struct mpls_label mpls_tmp, *mpls;
988
989 mpls = skb_header_pointer(skb, sizeof(struct ethhdr),
990 sizeof(*mpls), &mpls_tmp);
991 if (!mpls)
992 return 0;
993
994 ret = (ntohl(mpls->entry) & MPLS_LS_TC_MASK)
995 >> MPLS_LS_TC_SHIFT;
996 goto out;
997 }
998 case htons(ETH_P_80221):
999 /* 802.21 is always network control traffic */
1000 return 7;
1001 default:
1002 return 0;
1003 }
1004
1005 if (qos_map) {
1006 unsigned int i, tmp_dscp = dscp >> 2;
1007
1008 for (i = 0; i < qos_map->num_des; i++) {
1009 if (tmp_dscp == qos_map->dscp_exception[i].dscp) {
1010 ret = qos_map->dscp_exception[i].up;
1011 goto out;
1012 }
1013 }
1014
1015 for (i = 0; i < 8; i++) {
1016 if (tmp_dscp >= qos_map->up[i].low &&
1017 tmp_dscp <= qos_map->up[i].high) {
1018 ret = i;
1019 goto out;
1020 }
1021 }
1022 }
1023
1024 /* The default mapping as defined Section 2.3 in RFC8325: The three
1025 * Most Significant Bits (MSBs) of the DSCP are used as the
1026 * corresponding L2 markings.
1027 */
1028 ret = dscp >> 5;
1029
1030 /* Handle specific DSCP values for which the default mapping (as
1031 * described above) doesn't adhere to the intended usage of the DSCP
1032 * value. See section 4 in RFC8325. Specifically, for the following
1033 * Diffserv Service Classes no update is needed:
1034 * - Standard: DF
1035 * - Low Priority Data: CS1
1036 * - Multimedia Conferencing: AF41, AF42, AF43
1037 * - Network Control Traffic: CS7
1038 * - Real-Time Interactive: CS4
1039 * - Signaling: CS5
1040 */
1041 switch (dscp >> 2) {
1042 case 10:
1043 case 12:
1044 case 14:
1045 /* High throughput data: AF11, AF12, AF13 */
1046 ret = 0;
1047 break;
1048 case 16:
1049 /* Operations, Administration, and Maintenance and Provisioning:
1050 * CS2
1051 */
1052 ret = 0;
1053 break;
1054 case 18:
1055 case 20:
1056 case 22:
1057 /* Low latency data: AF21, AF22, AF23 */
1058 ret = 3;
1059 break;
1060 case 24:
1061 /* Broadcasting video: CS3 */
1062 ret = 4;
1063 break;
1064 case 26:
1065 case 28:
1066 case 30:
1067 /* Multimedia Streaming: AF31, AF32, AF33 */
1068 ret = 4;
1069 break;
1070 case 44:
1071 /* Voice Admit: VA */
1072 ret = 6;
1073 break;
1074 case 46:
1075 /* Telephony traffic: EF */
1076 ret = 6;
1077 break;
1078 case 48:
1079 /* Network Control Traffic: CS6 */
1080 ret = 7;
1081 break;
1082 }
1083 out:
1084 return array_index_nospec(ret, IEEE80211_NUM_TIDS);
1085 }
1086 EXPORT_SYMBOL(cfg80211_classify8021d);
1087
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
On Tue, 2025-11-18 at 20:35 +0530, Ranganath V N wrote:
>
> switch (skb->protocol) {
> case htons(ETH_P_IP):
> + struct iphdr iph, *ip;
> +
> + ip = skb_header_pointer(skb, sizeof(struct ethhdr),
> + sizeof(*ip), &iph);
> + if (!ip)
> + return 0;
> +
> dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc;
If you pull it out that way, seems you should also _use_ it?
johannes
© 2016 - 2025 Red Hat, Inc.