ebt_redirect_tg() dereferences br_port_get_rcu() return without a NULL check, causing a kernel panic when the bridge port has been removed between the original hook invocation and an NFQUEUE reinject. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Hacking Signed-off-by: Florian Westphal --- net/bridge/netfilter/ebt_redirect.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index 307790562b49..379662961aeb 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -20,16 +20,25 @@ static unsigned int ebt_redirect_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_redirect_info *info = par->targinfo; + const unsigned char *dev_addr; if (skb_ensure_writable(skb, 0)) return EBT_DROP; - if (xt_hooknum(par) != NF_BR_BROUTING) - /* rcu_read_lock()ed by nf_hook_thresh */ - ether_addr_copy(eth_hdr(skb)->h_dest, - br_port_get_rcu(xt_in(par))->br->dev->dev_addr); - else - ether_addr_copy(eth_hdr(skb)->h_dest, xt_in(par)->dev_addr); + if (xt_hooknum(par) != NF_BR_BROUTING) { + const struct net_bridge_port *port; + + port = br_port_get_rcu(xt_in(par)); + if (!port) + return EBT_DROP; + + dev_addr = port->br->dev->dev_addr; + } else { + dev_addr = xt_in(par)->dev_addr; + } + + ether_addr_copy(eth_hdr(skb)->h_dest, dev_addr); + skb->pkt_type = PACKET_HOST; return info->target; } -- 2.54.0