Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'bridge-stale-ptrs'

Nikolay Aleksandrov says:

====================
net: bridge: fix possible stale skb pointers

In the bridge driver we have a couple of places which call pskb_may_pull
but we've cached skb pointers before that and use them after which can
lead to out-of-bounds/stale pointer use. I've had these in my "to fix"
list for some time and now we got a report (patch 01) so here they are.
Patches 02-04 are fixes based on code inspection. Also patch 01 was
tested by Martin Weinelt, Martin if you don't mind please add your
tested-by tag to it by replying with Tested-by: name <email>.
I've also briefly tested the set by trying to exercise those code paths.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+17 -17
+3 -5
net/bridge/br_input.c
··· 74 74 struct net_bridge_fdb_entry *dst = NULL; 75 75 struct net_bridge_mdb_entry *mdst; 76 76 bool local_rcv, mcast_hit = false; 77 - const unsigned char *dest; 78 77 struct net_bridge *br; 79 78 u16 vid = 0; 80 79 ··· 91 92 br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, false); 92 93 93 94 local_rcv = !!(br->dev->flags & IFF_PROMISC); 94 - dest = eth_hdr(skb)->h_dest; 95 - if (is_multicast_ether_addr(dest)) { 95 + if (is_multicast_ether_addr(eth_hdr(skb)->h_dest)) { 96 96 /* by definition the broadcast is also a multicast address */ 97 - if (is_broadcast_ether_addr(dest)) { 97 + if (is_broadcast_ether_addr(eth_hdr(skb)->h_dest)) { 98 98 pkt_type = BR_PKT_BROADCAST; 99 99 local_rcv = true; 100 100 } else { ··· 143 145 } 144 146 break; 145 147 case BR_PKT_UNICAST: 146 - dst = br_fdb_find_rcu(br, dest, vid); 148 + dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid); 147 149 default: 148 150 break; 149 151 }
+13 -10
net/bridge/br_multicast.c
··· 911 911 int type; 912 912 int err = 0; 913 913 __be32 group; 914 + u16 nsrcs; 914 915 915 916 ih = igmpv3_report_hdr(skb); 916 917 num = ntohs(ih->ngrec); ··· 925 924 grec = (void *)(skb->data + len - sizeof(*grec)); 926 925 group = grec->grec_mca; 927 926 type = grec->grec_type; 927 + nsrcs = ntohs(grec->grec_nsrcs); 928 928 929 - len += ntohs(grec->grec_nsrcs) * 4; 929 + len += nsrcs * 4; 930 930 if (!ip_mc_may_pull(skb, len)) 931 931 return -EINVAL; 932 932 ··· 948 946 src = eth_hdr(skb)->h_source; 949 947 if ((type == IGMPV3_CHANGE_TO_INCLUDE || 950 948 type == IGMPV3_MODE_IS_INCLUDE) && 951 - ntohs(grec->grec_nsrcs) == 0) { 949 + nsrcs == 0) { 952 950 br_ip4_multicast_leave_group(br, port, group, vid, src); 953 951 } else { 954 952 err = br_ip4_multicast_add_group(br, port, group, vid, ··· 985 983 len = skb_transport_offset(skb) + sizeof(*icmp6h); 986 984 987 985 for (i = 0; i < num; i++) { 988 - __be16 *nsrcs, _nsrcs; 986 + __be16 *_nsrcs, __nsrcs; 987 + u16 nsrcs; 989 988 990 989 nsrcs_offset = len + offsetof(struct mld2_grec, grec_nsrcs); 991 990 ··· 994 991 nsrcs_offset + sizeof(_nsrcs)) 995 992 return -EINVAL; 996 993 997 - nsrcs = skb_header_pointer(skb, nsrcs_offset, 998 - sizeof(_nsrcs), &_nsrcs); 999 - if (!nsrcs) 994 + _nsrcs = skb_header_pointer(skb, nsrcs_offset, 995 + sizeof(__nsrcs), &__nsrcs); 996 + if (!_nsrcs) 1000 997 return -EINVAL; 1001 998 1002 - grec_len = struct_size(grec, grec_src, ntohs(*nsrcs)); 999 + nsrcs = ntohs(*_nsrcs); 1000 + grec_len = struct_size(grec, grec_src, nsrcs); 1003 1001 1004 1002 if (!ipv6_mc_may_pull(skb, len + grec_len)) 1005 1003 return -EINVAL; ··· 1025 1021 src = eth_hdr(skb)->h_source; 1026 1022 if ((grec->grec_type == MLD2_CHANGE_TO_INCLUDE || 1027 1023 grec->grec_type == MLD2_MODE_IS_INCLUDE) && 1028 - ntohs(*nsrcs) == 0) { 1024 + nsrcs == 0) { 1029 1025 br_ip6_multicast_leave_group(br, port, &grec->grec_mca, 1030 1026 vid, src); 1031 1027 } else { ··· 1279 1275 u16 vid) 1280 1276 { 1281 1277 unsigned int transport_len = ipv6_transport_len(skb); 1282 - const struct ipv6hdr *ip6h = ipv6_hdr(skb); 1283 1278 struct mld_msg *mld; 1284 1279 struct net_bridge_mdb_entry *mp; 1285 1280 struct mld2_query *mld2q; ··· 1322 1319 1323 1320 if (is_general_query) { 1324 1321 saddr.proto = htons(ETH_P_IPV6); 1325 - saddr.u.ip6 = ip6h->saddr; 1322 + saddr.u.ip6 = ipv6_hdr(skb)->saddr; 1326 1323 1327 1324 br_multicast_query_received(br, port, &br->ip6_other_query, 1328 1325 &saddr, max_delay);
+1 -2
net/bridge/br_stp_bpdu.c
··· 143 143 void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb, 144 144 struct net_device *dev) 145 145 { 146 - const unsigned char *dest = eth_hdr(skb)->h_dest; 147 146 struct net_bridge_port *p; 148 147 struct net_bridge *br; 149 148 const unsigned char *buf; ··· 171 172 if (p->state == BR_STATE_DISABLED) 172 173 goto out; 173 174 174 - if (!ether_addr_equal(dest, br->group_addr)) 175 + if (!ether_addr_equal(eth_hdr(skb)->h_dest, br->group_addr)) 175 176 goto out; 176 177 177 178 if (p->flags & BR_BPDU_GUARD) {