Skip to content

Commit 9874808

Browse files
Snorchummakynes
authored andcommitted
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: c4e70a8 ("netfilter: bridge: rename br_netfilter.c to br_netfilter_hooks.c") Suggested-by: Florian Westphal <[email protected]> Signed-off-by: Pavel Tikhomirov <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent a54e721 commit 9874808

File tree

6 files changed

+61
-21
lines changed

6 files changed

+61
-21
lines changed

include/linux/netfilter_bridge.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ static inline int nf_bridge_get_physinif(const struct sk_buff *skb)
4242
if (!nf_bridge)
4343
return 0;
4444

45-
return nf_bridge->physindev ? nf_bridge->physindev->ifindex : 0;
45+
return nf_bridge->physinif;
4646
}
4747

4848
static inline int nf_bridge_get_physoutif(const struct sk_buff *skb)
@@ -60,7 +60,7 @@ nf_bridge_get_physindev(const struct sk_buff *skb, struct net *net)
6060
{
6161
const struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
6262

63-
return nf_bridge ? nf_bridge->physindev : NULL;
63+
return nf_bridge ? dev_get_by_index_rcu(net, nf_bridge->physinif) : NULL;
6464
}
6565

6666
static inline struct net_device *

include/linux/skbuff.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ struct nf_bridge_info {
295295
u8 bridged_dnat:1;
296296
u8 sabotage_in_done:1;
297297
__u16 frag_max_size;
298-
struct net_device *physindev;
298+
int physinif;
299299

300300
/* always valid & non-NULL from FORWARD on, for physdev match */
301301
struct net_device *physoutdev;

net/bridge/br_netfilter_hooks.c

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,17 @@ int br_nf_pre_routing_finish_bridge(struct net *net, struct sock *sk, struct sk_
279279

280280
if ((READ_ONCE(neigh->nud_state) & NUD_CONNECTED) &&
281281
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+
282290
neigh_hh_bridge(&neigh->hh, skb);
283-
skb->dev = nf_bridge->physindev;
291+
skb->dev = br_indev;
292+
284293
ret = br_handle_frame_finish(net, sk, skb);
285294
} else {
286295
/* the neighbour function below overwrites the complete
@@ -352,12 +361,18 @@ br_nf_ipv4_daddr_was_changed(const struct sk_buff *skb,
352361
*/
353362
static int br_nf_pre_routing_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
354363
{
355-
struct net_device *dev = skb->dev;
364+
struct net_device *dev = skb->dev, *br_indev;
356365
struct iphdr *iph = ip_hdr(skb);
357366
struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
358367
struct rtable *rt;
359368
int err;
360369

370+
br_indev = nf_bridge_get_physindev(skb, net);
371+
if (!br_indev) {
372+
kfree_skb(skb);
373+
return 0;
374+
}
375+
361376
nf_bridge->frag_max_size = IPCB(skb)->frag_max_size;
362377

363378
if (nf_bridge->pkt_otherhost) {
@@ -397,7 +412,7 @@ static int br_nf_pre_routing_finish(struct net *net, struct sock *sk, struct sk_
397412
} else {
398413
if (skb_dst(skb)->dev == dev) {
399414
bridged_dnat:
400-
skb->dev = nf_bridge->physindev;
415+
skb->dev = br_indev;
401416
nf_bridge_update_protocol(skb);
402417
nf_bridge_push_encap_header(skb);
403418
br_nf_hook_thresh(NF_BR_PRE_ROUTING,
@@ -410,7 +425,7 @@ static int br_nf_pre_routing_finish(struct net *net, struct sock *sk, struct sk_
410425
skb->pkt_type = PACKET_HOST;
411426
}
412427
} else {
413-
rt = bridge_parent_rtable(nf_bridge->physindev);
428+
rt = bridge_parent_rtable(br_indev);
414429
if (!rt) {
415430
kfree_skb(skb);
416431
return 0;
@@ -419,7 +434,7 @@ static int br_nf_pre_routing_finish(struct net *net, struct sock *sk, struct sk_
419434
skb_dst_set_noref(skb, &rt->dst);
420435
}
421436

422-
skb->dev = nf_bridge->physindev;
437+
skb->dev = br_indev;
423438
nf_bridge_update_protocol(skb);
424439
nf_bridge_push_encap_header(skb);
425440
br_nf_hook_thresh(NF_BR_PRE_ROUTING, net, sk, skb, skb->dev, NULL,
@@ -456,7 +471,7 @@ struct net_device *setup_pre_routing(struct sk_buff *skb, const struct net *net)
456471
}
457472

458473
nf_bridge->in_prerouting = 1;
459-
nf_bridge->physindev = skb->dev;
474+
nf_bridge->physinif = skb->dev->ifindex;
460475
skb->dev = brnf_get_logical_dev(skb, skb->dev, net);
461476

462477
if (skb->protocol == htons(ETH_P_8021Q))
@@ -553,7 +568,11 @@ static int br_nf_forward_finish(struct net *net, struct sock *sk, struct sk_buff
553568
if (skb->protocol == htons(ETH_P_IPV6))
554569
nf_bridge->frag_max_size = IP6CB(skb)->frag_max_size;
555570

556-
in = nf_bridge->physindev;
571+
in = nf_bridge_get_physindev(skb, net);
572+
if (!in) {
573+
kfree_skb(skb);
574+
return 0;
575+
}
557576
if (nf_bridge->pkt_otherhost) {
558577
skb->pkt_type = PACKET_OTHERHOST;
559578
nf_bridge->pkt_otherhost = false;
@@ -899,6 +918,13 @@ static unsigned int ip_sabotage_in(void *priv,
899918
static void br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb)
900919
{
901920
struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
921+
struct net_device *br_indev;
922+
923+
br_indev = nf_bridge_get_physindev(skb, dev_net(skb->dev));
924+
if (!br_indev) {
925+
kfree_skb(skb);
926+
return;
927+
}
902928

903929
skb_pull(skb, ETH_HLEN);
904930
nf_bridge->bridged_dnat = 0;
@@ -908,7 +934,7 @@ static void br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb)
908934
skb_copy_to_linear_data_offset(skb, -(ETH_HLEN - ETH_ALEN),
909935
nf_bridge->neigh_header,
910936
ETH_HLEN - ETH_ALEN);
911-
skb->dev = nf_bridge->physindev;
937+
skb->dev = br_indev;
912938

913939
nf_bridge->physoutdev = NULL;
914940
br_handle_frame_finish(dev_net(skb->dev), NULL, skb);

net/bridge/br_netfilter_ipv6.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,15 @@ static int br_nf_pre_routing_finish_ipv6(struct net *net, struct sock *sk, struc
102102
{
103103
struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
104104
struct rtable *rt;
105-
struct net_device *dev = skb->dev;
105+
struct net_device *dev = skb->dev, *br_indev;
106106
const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops();
107107

108+
br_indev = nf_bridge_get_physindev(skb, net);
109+
if (!br_indev) {
110+
kfree_skb(skb);
111+
return 0;
112+
}
113+
108114
nf_bridge->frag_max_size = IP6CB(skb)->frag_max_size;
109115

110116
if (nf_bridge->pkt_otherhost) {
@@ -122,7 +128,7 @@ static int br_nf_pre_routing_finish_ipv6(struct net *net, struct sock *sk, struc
122128
}
123129

124130
if (skb_dst(skb)->dev == dev) {
125-
skb->dev = nf_bridge->physindev;
131+
skb->dev = br_indev;
126132
nf_bridge_update_protocol(skb);
127133
nf_bridge_push_encap_header(skb);
128134
br_nf_hook_thresh(NF_BR_PRE_ROUTING,
@@ -133,7 +139,7 @@ static int br_nf_pre_routing_finish_ipv6(struct net *net, struct sock *sk, struc
133139
ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);
134140
skb->pkt_type = PACKET_HOST;
135141
} else {
136-
rt = bridge_parent_rtable(nf_bridge->physindev);
142+
rt = bridge_parent_rtable(br_indev);
137143
if (!rt) {
138144
kfree_skb(skb);
139145
return 0;
@@ -142,7 +148,7 @@ static int br_nf_pre_routing_finish_ipv6(struct net *net, struct sock *sk, struc
142148
skb_dst_set_noref(skb, &rt->dst);
143149
}
144150

145-
skb->dev = nf_bridge->physindev;
151+
skb->dev = br_indev;
146152
nf_bridge_update_protocol(skb);
147153
nf_bridge_push_encap_header(skb);
148154
br_nf_hook_thresh(NF_BR_PRE_ROUTING, net, sk, skb,

net/ipv4/netfilter/nf_reject_ipv4.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,6 @@ static int nf_reject_fill_skb_dst(struct sk_buff *skb_in)
239239
void nf_send_reset(struct net *net, struct sock *sk, struct sk_buff *oldskb,
240240
int hook)
241241
{
242-
struct net_device *br_indev __maybe_unused;
243242
struct sk_buff *nskb;
244243
struct iphdr *niph;
245244
const struct tcphdr *oth;
@@ -289,9 +288,13 @@ void nf_send_reset(struct net *net, struct sock *sk, struct sk_buff *oldskb,
289288
* build the eth header using the original destination's MAC as the
290289
* source, and send the RST packet directly.
291290
*/
292-
br_indev = nf_bridge_get_physindev(oldskb, net);
293-
if (br_indev) {
291+
if (nf_bridge_info_exists(oldskb)) {
294292
struct ethhdr *oeth = eth_hdr(oldskb);
293+
struct net_device *br_indev;
294+
295+
br_indev = nf_bridge_get_physindev(oldskb, net);
296+
if (!br_indev)
297+
goto free_nskb;
295298

296299
nskb->dev = br_indev;
297300
niph->tot_len = htons(nskb->len);

net/ipv6/netfilter/nf_reject_ipv6.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,6 @@ static int nf_reject6_fill_skb_dst(struct sk_buff *skb_in)
278278
void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb,
279279
int hook)
280280
{
281-
struct net_device *br_indev __maybe_unused;
282281
struct sk_buff *nskb;
283282
struct tcphdr _otcph;
284283
const struct tcphdr *otcph;
@@ -354,9 +353,15 @@ void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb,
354353
* build the eth header using the original destination's MAC as the
355354
* source, and send the RST packet directly.
356355
*/
357-
br_indev = nf_bridge_get_physindev(oldskb, net);
358-
if (br_indev) {
356+
if (nf_bridge_info_exists(oldskb)) {
359357
struct ethhdr *oeth = eth_hdr(oldskb);
358+
struct net_device *br_indev;
359+
360+
br_indev = nf_bridge_get_physindev(oldskb, net);
361+
if (!br_indev) {
362+
kfree_skb(nskb);
363+
return;
364+
}
360365

361366
nskb->dev = br_indev;
362367
nskb->protocol = htons(ETH_P_IPV6);

0 commit comments

Comments
 (0)