We need to (re-)init physoutdev to NULL, as its storage area is used to stash the ip destination to detect L3 nat. For frames that are bridged this is no problem because the bridge forward hook initializes physoutdev to the real bridge port. But in case the skb is delivered locally and then routed we can crash in the physdev match since nf_bridge->physoutdev is garbage (ipv4/ipv6 address) Fixes: 72b1e5e4cac7 ("netfilter: bridge: reduce nf_bridge_info to 32 bytes again") Reported-and-tested-by: Sander Eikelenboom <linux@xxxxxxxxxxxxxx> Signed-off-by: Florian Westphal <fw@xxxxxxxxx> --- net/bridge/br_netfilter_hooks.c | 10 +++++++++- net/bridge/br_netfilter_ipv6.c | 7 ++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 33a82ff..824fbc8 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -355,6 +355,7 @@ static int br_nf_pre_routing_finish(struct sock *sk, struct sk_buff *skb) struct iphdr *iph = ip_hdr(skb); struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb); struct rtable *rt; + bool daddr_changed; int err; nf_bridge->frag_max_size = IPCB(skb)->frag_max_size; @@ -363,8 +364,15 @@ static int br_nf_pre_routing_finish(struct sock *sk, struct sk_buff *skb) skb->pkt_type = PACKET_OTHERHOST; nf_bridge->pkt_otherhost = false; } + + daddr_changed = br_nf_ipv4_daddr_was_changed(skb, nf_bridge); + /* init physoutdev to NULL. Its set by the bridge forward hook, but + * frame might be routed instead of bridged. + */ + nf_bridge->physoutdev = NULL; nf_bridge->in_prerouting = 0; - if (br_nf_ipv4_daddr_was_changed(skb, nf_bridge)) { + + if (daddr_changed) { if ((err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))) { struct in_device *in_dev = __in_dev_get_rcu(dev); diff --git a/net/bridge/br_netfilter_ipv6.c b/net/bridge/br_netfilter_ipv6.c index 77383bf..772222b 100644 --- a/net/bridge/br_netfilter_ipv6.c +++ b/net/bridge/br_netfilter_ipv6.c @@ -167,6 +167,7 @@ static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb) struct rtable *rt; struct net_device *dev = skb->dev; const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops(); + bool daddr_changed; nf_bridge->frag_max_size = IP6CB(skb)->frag_max_size; @@ -174,8 +175,12 @@ static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb) skb->pkt_type = PACKET_OTHERHOST; nf_bridge->pkt_otherhost = false; } + + daddr_changed = br_nf_ipv6_daddr_was_changed(skb, nf_bridge); + nf_bridge->physoutdev = NULL; nf_bridge->in_prerouting = 0; - if (br_nf_ipv6_daddr_was_changed(skb, nf_bridge)) { + + if (daddr_changed) { skb_dst_drop(skb); v6ops->route_input(skb); -- 2.0.5 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html