[PATCH v3] wifi: cfg80211: Fix uninitialized header access in cfg80211_classify8021d

Ranganath V N posted 1 patch 1 week, 6 days ago
net/wireless/util.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
[PATCH v3] wifi: cfg80211: Fix uninitialized header access in cfg80211_classify8021d
Posted by Ranganath V N 1 week, 6 days ago
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
Re: [PATCH v3] wifi: cfg80211: Fix uninitialized header access in cfg80211_classify8021d
Posted by kernel test robot 1 week, 5 days ago
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
Re: [PATCH v3] wifi: cfg80211: Fix uninitialized header access in cfg80211_classify8021d
Posted by Johannes Berg 1 week, 5 days ago
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