All of lore.kernel.org
 help / color / mirror / Atom feed
From: Florian Westphal <fw@strlen.de>
To: Kyle Zeng <kylebot@openai.com>
Cc: netfilter-devel@vger.kernel.org,
	Pablo Neira Ayuso <pablo@netfilter.org>,
	coreteam@netfilter.org, bridge@lists.linux.dev,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	outbounddisclosures@openai.com
Subject: Re: [PATCH] netfilter: br_netfilter: reject unsafe neighbour devices for DNAT
Date: Sat, 7 Nov 2026 08:25:41 +0100	[thread overview]
Message-ID: <au7SdYQrIqAQOU24@strlen.de> (raw)
In-Reply-To: <20261030042155.73844-1-kylebot@openai.com>

Kyle Zeng <kylebot@openai.com> wrote:
> The IPv4 and IPv6 bridge prerouting callbacks classify DNAT traffic using
> the logical input device saved in skb->dev. The physical input port can
> have a different bridge master when prerouting resumes, for example after
> an NFQUEUE verdict. Netfilter reassembly can also combine the first
> fragment's bridge information with a different fragment's input device.
> 
> br_nf_pre_routing_finish_bridge() replaces skb->dev with the port's
> current bridge master, but looks up the neighbour using the existing
> route. This can put an skb with skb->dev pointing to bridge B on an
> unresolved neighbour of bridge A. Deleting B and resolving the neighbour
> on A then causes a use-after-free in __dev_queue_xmit().
> 
> Before calling the neighbour output method, check that the neighbour
> keeps skb->dev alive. A neighbour holds its device, and a VLAN device
> also holds its real device, so allow a VLAN whose real device is the
> bridge. Release the neighbour and drop the skb on a mismatch.
> 
> Doing this in the shared continuation covers both protocol families,
> including a queue in the remaining bridge prerouting hooks. Leave the
> cached-header path unchanged since it does not enqueue the skb.
> 
> Signed-off-by: Kyle Zeng <kylebot@openai.com>
> ---
> I have a reproducer for this issue and can share via DM
> 
>  net/bridge/br_netfilter_hooks.c | 12 ++++++++++++
>  1 file changed, 12 insertions(+)
> 
> diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
> index e15355ff215a..726a2d17ed3a 100644
> --- a/net/bridge/br_netfilter_hooks.c
> +++ b/net/bridge/br_netfilter_hooks.c
> @@ -307,6 +307,18 @@ int br_nf_pre_routing_finish_bridge(struct net *net, struct sock *sk, struct sk_
>  
>  			ret = br_handle_frame_finish(net, sk, skb);
>  		} else {
> +			struct net_device *dev = neigh->dev;
> +
> +			/* The neighbour queue must keep skb->dev alive. A VLAN
> +			 * device also holds a reference to its real device.
> +			 */
> +			if (is_vlan_dev(dev))
> +				dev = vlan_dev_real_dev(dev);
> +			if (dev != skb->dev) {
> +				neigh_release(neigh);
> +				goto free_skb;
> +			}
> +

I don't understand the vlan check. Why is that needed?
The call to br_nf_pre_routing_finish_bridge is gated via:

 405                         if (skb_dst(skb)->dev == dev) {
 406                                 skb->dev = br_indev;
 407                                 nf_bridge_update_protocol(skb);
 408                                 nf_bridge_push_encap_header(skb);
 409                                 br_nf_hook_thresh(NF_BR_PRE_ROUTING,
 410                                                   net, sk, skb, skb->dev,
 411                                                   NULL,
 412                                                   br_nf_pre_routing_finish_bridge);


... so this patch ought to be sufficient?

--- a/net/bridge/br_netfilter_hooks.c
+++ b/net/bridge/br_netfilter_hooks.c
@@ -283,6 +283,10 @@ int br_nf_pre_routing_finish_bridge(struct net *net, struct sock *sk, struct sk_
        if (!skb->dev)
                goto free_skb;
        dst = skb_dst(skb);
+
+       if (dst->dev != skb->dev)
+               goto free_skb;
+
        neigh = dst_neigh_lookup_skb(dst, skb);

(I can't see how neigh->dev != skb->dev after the extra test).

  parent reply	other threads:[~2026-11-07  7:25 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30  4:21 [PATCH] netfilter: br_netfilter: reject unsafe neighbour devices for DNAT Kyle Zeng
2026-10-30  4:23 ` netdev-bot+sinfo
2026-10-31 18:50 ` Florian Westphal
2026-11-07  7:25 ` Florian Westphal [this message]
2026-11-07 10:28   ` Kyle Zeng
2026-11-07 12:10     ` Florian Westphal

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=au7SdYQrIqAQOU24@strlen.de \
    --to=fw@strlen.de \
    --cc=bridge@lists.linux.dev \
    --cc=coreteam@netfilter.org \
    --cc=kylebot@openai.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=outbounddisclosures@openai.com \
    --cc=pablo@netfilter.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.