wifi: cfg80211: fix ieee80211_data_to_8023_exthdr handling of small packets
authorFelix Fietkau <nbd@nbd.name>
Fri, 7 Oct 2022 12:56:11 +0000 (14:56 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 7 Oct 2022 12:57:20 +0000 (14:57 +0200)
STP topology change notification packets only have a payload of 7 bytes,
so they get dropped due to the skb->len < hdrlen + 8 check.
Fix this by removing the extra 8 from the skb->len check and checking the
return code on the skb_copy_bits calls.

Fixes: 2d1c304cb2d5 ("cfg80211: add function for 802.3 conversion with separate output buffer")
Reported-by: Chad Monroe <chad.monroe@smartrg.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/wireless/util.c

index 01493568a21dfe07d36377c2e4a1ca7c602ac037..1f285b51502866439da976109764fbe4362bee60 100644 (file)
@@ -559,7 +559,7 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
                return -1;
 
        hdrlen = ieee80211_hdrlen(hdr->frame_control) + data_offset;
-       if (skb->len < hdrlen + 8)
+       if (skb->len < hdrlen)
                return -1;
 
        /* convert IEEE 802.11 header + possible LLC headers into Ethernet
@@ -574,8 +574,9 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
        memcpy(tmp.h_dest, ieee80211_get_DA(hdr), ETH_ALEN);
        memcpy(tmp.h_source, ieee80211_get_SA(hdr), ETH_ALEN);
 
-       if (iftype == NL80211_IFTYPE_MESH_POINT)
-               skb_copy_bits(skb, hdrlen, &mesh_flags, 1);
+       if (iftype == NL80211_IFTYPE_MESH_POINT &&
+           skb_copy_bits(skb, hdrlen, &mesh_flags, 1) < 0)
+               return -1;
 
        mesh_flags &= MESH_FLAGS_AE;
 
@@ -595,11 +596,12 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
                if (iftype == NL80211_IFTYPE_MESH_POINT) {
                        if (mesh_flags == MESH_FLAGS_AE_A4)
                                return -1;
-                       if (mesh_flags == MESH_FLAGS_AE_A5_A6) {
-                               skb_copy_bits(skb, hdrlen +
-                                       offsetof(struct ieee80211s_hdr, eaddr1),
-                                       tmp.h_dest, 2 * ETH_ALEN);
-                       }
+                       if (mesh_flags == MESH_FLAGS_AE_A5_A6 &&
+                           skb_copy_bits(skb, hdrlen +
+                                         offsetof(struct ieee80211s_hdr, eaddr1),
+                                         tmp.h_dest, 2 * ETH_ALEN) < 0)
+                               return -1;
+
                        hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
                }
                break;
@@ -613,10 +615,11 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
                if (iftype == NL80211_IFTYPE_MESH_POINT) {
                        if (mesh_flags == MESH_FLAGS_AE_A5_A6)
                                return -1;
-                       if (mesh_flags == MESH_FLAGS_AE_A4)
-                               skb_copy_bits(skb, hdrlen +
-                                       offsetof(struct ieee80211s_hdr, eaddr1),
-                                       tmp.h_source, ETH_ALEN);
+                       if (mesh_flags == MESH_FLAGS_AE_A4 &&
+                           skb_copy_bits(skb, hdrlen +
+                                         offsetof(struct ieee80211s_hdr, eaddr1),
+                                         tmp.h_source, ETH_ALEN) < 0)
+                               return -1;
                        hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
                }
                break;
@@ -628,16 +631,15 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
                break;
        }
 
-       skb_copy_bits(skb, hdrlen, &payload, sizeof(payload));
-       tmp.h_proto = payload.proto;
-
-       if (likely((!is_amsdu && ether_addr_equal(payload.hdr, rfc1042_header) &&
-                   tmp.h_proto != htons(ETH_P_AARP) &&
-                   tmp.h_proto != htons(ETH_P_IPX)) ||
-                  ether_addr_equal(payload.hdr, bridge_tunnel_header))) {
+       if (likely(skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)) == 0 &&
+                  ((!is_amsdu && ether_addr_equal(payload.hdr, rfc1042_header) &&
+                    payload.proto != htons(ETH_P_AARP) &&
+                    payload.proto != htons(ETH_P_IPX)) ||
+                   ether_addr_equal(payload.hdr, bridge_tunnel_header)))) {
                /* remove RFC1042 or Bridge-Tunnel encapsulation and
                 * replace EtherType */
                hdrlen += ETH_ALEN + 2;
+               tmp.h_proto = payload.proto;
                skb_postpull_rcsum(skb, &payload, ETH_ALEN + 2);
        } else {
                tmp.h_proto = htons(skb->len - hdrlen);