Skip to content

Commit 7f7708f

Browse files
michael-devdavem330
authored andcommitted
bridge: Fix br_forward crash in promiscuous mode
From: Michael Braun <[email protected]> bridge: Fix br_forward crash in promiscuous mode It's a linux-next kernel from 2010-03-12 on an x86 system and it OOPs in the bridge module in br_pass_frame_up (called by br_handle_frame_finish) because brdev cannot be dereferenced (its set to a non-null value). Adding some BUG_ON statements revealed that BR_INPUT_SKB_CB(skb)->brdev == br-dev (as set in br_handle_frame_finish first) only holds until br_forward is called. The next call to br_pass_frame_up then fails. Digging deeper it seems that br_forward either frees the skb or passes it to NF_HOOK which will in turn take care of freeing the skb. The same is holds for br_pass_frame_ip. So it seems as if two independent skb allocations are required. As far as I can see, commit b33084b ("bridge: Avoid unnecessary clone on forward path") removed skb duplication and so likely causes this crash. This crash does not happen on 2.6.33. I've therefore modified br_forward the same way br_flood has been modified so that the skb is not freed if skb0 is going to be used and I can confirm that the attached patch resolves the issue for me. Signed-off-by: Herbert Xu <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 0821ec5 commit 7f7708f

File tree

3 files changed

+13
-5
lines changed

3 files changed

+13
-5
lines changed

net/bridge/br_forward.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
#include <linux/netfilter_bridge.h>
2020
#include "br_private.h"
2121

22+
static int deliver_clone(struct net_bridge_port *prev, struct sk_buff *skb,
23+
void (*__packet_hook)(const struct net_bridge_port *p,
24+
struct sk_buff *skb));
25+
2226
/* Don't forward packets to originating port or forwarding diasabled */
2327
static inline int should_deliver(const struct net_bridge_port *p,
2428
const struct sk_buff *skb)
@@ -94,14 +98,18 @@ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
9498
}
9599

96100
/* called with rcu_read_lock */
97-
void br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
101+
void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0)
98102
{
99103
if (should_deliver(to, skb)) {
100-
__br_forward(to, skb);
104+
if (skb0)
105+
deliver_clone(to, skb, __br_forward);
106+
else
107+
__br_forward(to, skb);
101108
return;
102109
}
103110

104-
kfree_skb(skb);
111+
if (!skb0)
112+
kfree_skb(skb);
105113
}
106114

107115
static int deliver_clone(struct net_bridge_port *prev, struct sk_buff *skb,

net/bridge/br_input.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ int br_handle_frame_finish(struct sk_buff *skb)
9090

9191
if (skb) {
9292
if (dst)
93-
br_forward(dst->dst, skb);
93+
br_forward(dst->dst, skb, skb2);
9494
else
9595
br_flood_forward(br, skb, skb2);
9696
}

net/bridge/br_private.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ extern void br_deliver(const struct net_bridge_port *to,
252252
struct sk_buff *skb);
253253
extern int br_dev_queue_push_xmit(struct sk_buff *skb);
254254
extern void br_forward(const struct net_bridge_port *to,
255-
struct sk_buff *skb);
255+
struct sk_buff *skb, struct sk_buff *skb0);
256256
extern int br_forward_finish(struct sk_buff *skb);
257257
extern void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb);
258258
extern void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,

0 commit comments

Comments
 (0)