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

netfilter: bridge: replace physindev with physinif in nf_bridge_info

An skb can be added to a neigh->arp_queue while waiting for an arp
reply. Where original skb's skb->dev can be different to neigh's
neigh->dev. For instance in case of bridging dnated skb from one veth to
another, the skb would be added to a neigh->arp_queue of the bridge.

As skb->dev can be reset back to nf_bridge->physindev and used, and as
there is no explicit mechanism that prevents this physindev from been
freed under us (for instance neigh_flush_dev doesn't cleanup skbs from
different device's neigh queue) we can crash on e.g. this stack:

arp_process
neigh_update
skb = __skb_dequeue(&neigh->arp_queue)
neigh_resolve_output(..., skb)
...
br_nf_dev_xmit
br_nf_pre_routing_finish_bridge_slow
skb->dev = nf_bridge->physindev
br_handle_frame_finish

Let's use plain ifindex instead of net_device link. To peek into the
original net_device we will use dev_get_by_index_rcu(). Thus either we
get device and are safe to use it or we don't get it and drop skb.

Fixes: c4e70a87d975 ("netfilter: bridge: rename br_netfilter.c to br_netfilter_hooks.c")
Suggested-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Pavel Tikhomirov and committed by
Pablo Neira Ayuso
98748088 a54e7219

+61 -21
+2 -2
include/linux/netfilter_bridge.h
··· 42 42 if (!nf_bridge) 43 43 return 0; 44 44 45 - return nf_bridge->physindev ? nf_bridge->physindev->ifindex : 0; 45 + return nf_bridge->physinif; 46 46 } 47 47 48 48 static inline int nf_bridge_get_physoutif(const struct sk_buff *skb) ··· 60 60 { 61 61 const struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb); 62 62 63 - return nf_bridge ? nf_bridge->physindev : NULL; 63 + return nf_bridge ? dev_get_by_index_rcu(net, nf_bridge->physinif) : NULL; 64 64 } 65 65 66 66 static inline struct net_device *
+1 -1
include/linux/skbuff.h
··· 295 295 u8 bridged_dnat:1; 296 296 u8 sabotage_in_done:1; 297 297 __u16 frag_max_size; 298 - struct net_device *physindev; 298 + int physinif; 299 299 300 300 /* always valid & non-NULL from FORWARD on, for physdev match */ 301 301 struct net_device *physoutdev;
+34 -8
net/bridge/br_netfilter_hooks.c
··· 279 279 280 280 if ((READ_ONCE(neigh->nud_state) & NUD_CONNECTED) && 281 281 READ_ONCE(neigh->hh.hh_len)) { 282 + struct net_device *br_indev; 283 + 284 + br_indev = nf_bridge_get_physindev(skb, net); 285 + if (!br_indev) { 286 + neigh_release(neigh); 287 + goto free_skb; 288 + } 289 + 282 290 neigh_hh_bridge(&neigh->hh, skb); 283 - skb->dev = nf_bridge->physindev; 291 + skb->dev = br_indev; 292 + 284 293 ret = br_handle_frame_finish(net, sk, skb); 285 294 } else { 286 295 /* the neighbour function below overwrites the complete ··· 361 352 */ 362 353 static int br_nf_pre_routing_finish(struct net *net, struct sock *sk, struct sk_buff *skb) 363 354 { 364 - struct net_device *dev = skb->dev; 355 + struct net_device *dev = skb->dev, *br_indev; 365 356 struct iphdr *iph = ip_hdr(skb); 366 357 struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb); 367 358 struct rtable *rt; 368 359 int err; 360 + 361 + br_indev = nf_bridge_get_physindev(skb, net); 362 + if (!br_indev) { 363 + kfree_skb(skb); 364 + return 0; 365 + } 369 366 370 367 nf_bridge->frag_max_size = IPCB(skb)->frag_max_size; 371 368 ··· 412 397 } else { 413 398 if (skb_dst(skb)->dev == dev) { 414 399 bridged_dnat: 415 - skb->dev = nf_bridge->physindev; 400 + skb->dev = br_indev; 416 401 nf_bridge_update_protocol(skb); 417 402 nf_bridge_push_encap_header(skb); 418 403 br_nf_hook_thresh(NF_BR_PRE_ROUTING, ··· 425 410 skb->pkt_type = PACKET_HOST; 426 411 } 427 412 } else { 428 - rt = bridge_parent_rtable(nf_bridge->physindev); 413 + rt = bridge_parent_rtable(br_indev); 429 414 if (!rt) { 430 415 kfree_skb(skb); 431 416 return 0; ··· 434 419 skb_dst_set_noref(skb, &rt->dst); 435 420 } 436 421 437 - skb->dev = nf_bridge->physindev; 422 + skb->dev = br_indev; 438 423 nf_bridge_update_protocol(skb); 439 424 nf_bridge_push_encap_header(skb); 440 425 br_nf_hook_thresh(NF_BR_PRE_ROUTING, net, sk, skb, skb->dev, NULL, ··· 471 456 } 472 457 473 458 nf_bridge->in_prerouting = 1; 474 - nf_bridge->physindev = skb->dev; 459 + nf_bridge->physinif = skb->dev->ifindex; 475 460 skb->dev = brnf_get_logical_dev(skb, skb->dev, net); 476 461 477 462 if (skb->protocol == htons(ETH_P_8021Q)) ··· 568 553 if (skb->protocol == htons(ETH_P_IPV6)) 569 554 nf_bridge->frag_max_size = IP6CB(skb)->frag_max_size; 570 555 571 - in = nf_bridge->physindev; 556 + in = nf_bridge_get_physindev(skb, net); 557 + if (!in) { 558 + kfree_skb(skb); 559 + return 0; 560 + } 572 561 if (nf_bridge->pkt_otherhost) { 573 562 skb->pkt_type = PACKET_OTHERHOST; 574 563 nf_bridge->pkt_otherhost = false; ··· 918 899 static void br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb) 919 900 { 920 901 struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb); 902 + struct net_device *br_indev; 903 + 904 + br_indev = nf_bridge_get_physindev(skb, dev_net(skb->dev)); 905 + if (!br_indev) { 906 + kfree_skb(skb); 907 + return; 908 + } 921 909 922 910 skb_pull(skb, ETH_HLEN); 923 911 nf_bridge->bridged_dnat = 0; ··· 934 908 skb_copy_to_linear_data_offset(skb, -(ETH_HLEN - ETH_ALEN), 935 909 nf_bridge->neigh_header, 936 910 ETH_HLEN - ETH_ALEN); 937 - skb->dev = nf_bridge->physindev; 911 + skb->dev = br_indev; 938 912 939 913 nf_bridge->physoutdev = NULL; 940 914 br_handle_frame_finish(dev_net(skb->dev), NULL, skb);
+10 -4
net/bridge/br_netfilter_ipv6.c
··· 102 102 { 103 103 struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb); 104 104 struct rtable *rt; 105 - struct net_device *dev = skb->dev; 105 + struct net_device *dev = skb->dev, *br_indev; 106 106 const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops(); 107 + 108 + br_indev = nf_bridge_get_physindev(skb, net); 109 + if (!br_indev) { 110 + kfree_skb(skb); 111 + return 0; 112 + } 107 113 108 114 nf_bridge->frag_max_size = IP6CB(skb)->frag_max_size; 109 115 ··· 128 122 } 129 123 130 124 if (skb_dst(skb)->dev == dev) { 131 - skb->dev = nf_bridge->physindev; 125 + skb->dev = br_indev; 132 126 nf_bridge_update_protocol(skb); 133 127 nf_bridge_push_encap_header(skb); 134 128 br_nf_hook_thresh(NF_BR_PRE_ROUTING, ··· 139 133 ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr); 140 134 skb->pkt_type = PACKET_HOST; 141 135 } else { 142 - rt = bridge_parent_rtable(nf_bridge->physindev); 136 + rt = bridge_parent_rtable(br_indev); 143 137 if (!rt) { 144 138 kfree_skb(skb); 145 139 return 0; ··· 148 142 skb_dst_set_noref(skb, &rt->dst); 149 143 } 150 144 151 - skb->dev = nf_bridge->physindev; 145 + skb->dev = br_indev; 152 146 nf_bridge_update_protocol(skb); 153 147 nf_bridge_push_encap_header(skb); 154 148 br_nf_hook_thresh(NF_BR_PRE_ROUTING, net, sk, skb,
+6 -3
net/ipv4/netfilter/nf_reject_ipv4.c
··· 239 239 void nf_send_reset(struct net *net, struct sock *sk, struct sk_buff *oldskb, 240 240 int hook) 241 241 { 242 - struct net_device *br_indev __maybe_unused; 243 242 struct sk_buff *nskb; 244 243 struct iphdr *niph; 245 244 const struct tcphdr *oth; ··· 288 289 * build the eth header using the original destination's MAC as the 289 290 * source, and send the RST packet directly. 290 291 */ 291 - br_indev = nf_bridge_get_physindev(oldskb, net); 292 - if (br_indev) { 292 + if (nf_bridge_info_exists(oldskb)) { 293 293 struct ethhdr *oeth = eth_hdr(oldskb); 294 + struct net_device *br_indev; 295 + 296 + br_indev = nf_bridge_get_physindev(oldskb, net); 297 + if (!br_indev) 298 + goto free_nskb; 294 299 295 300 nskb->dev = br_indev; 296 301 niph->tot_len = htons(nskb->len);
+8 -3
net/ipv6/netfilter/nf_reject_ipv6.c
··· 278 278 void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb, 279 279 int hook) 280 280 { 281 - struct net_device *br_indev __maybe_unused; 282 281 struct sk_buff *nskb; 283 282 struct tcphdr _otcph; 284 283 const struct tcphdr *otcph; ··· 353 354 * build the eth header using the original destination's MAC as the 354 355 * source, and send the RST packet directly. 355 356 */ 356 - br_indev = nf_bridge_get_physindev(oldskb, net); 357 - if (br_indev) { 357 + if (nf_bridge_info_exists(oldskb)) { 358 358 struct ethhdr *oeth = eth_hdr(oldskb); 359 + struct net_device *br_indev; 360 + 361 + br_indev = nf_bridge_get_physindev(oldskb, net); 362 + if (!br_indev) { 363 + kfree_skb(nskb); 364 + return; 365 + } 359 366 360 367 nskb->dev = br_indev; 361 368 nskb->protocol = htons(ETH_P_IPV6);