mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Florian Westphal <fw@strlen.de>
To: Zihan Xi <zihanx@nebusec.ai>
Cc: linux-kernel@vger.kernel.org, netfilter-devel@vger.kernel.org,
	mingo@redhat.com, peterz@infradead.org, juri.lelli@redhat.com,
	vincent.guittot@linaro.org, dietmar.eggemann@arm.com,
	rostedt@goodmis.org, bsegall@google.com, mgorman@suse.de,
	vschneid@redhat.com, kprateek.nayak@amd.com, pablo@netfilter.org,
	phil@nwl.cc, davem@davemloft.net, edumazet@kernel.org,
	pabeni@redhat.com, horms@kernel.org, kaber@trash.net,
	jengelh@medozas.de, lukas@wunner.de, vega@nebusec.ai,
	root@tr0jan.top
Subject: Re: [PATCH nf v7 1/1] netfilter: nf_dup: preserve duplication guard across NFQUEUE
Date: Sun, 11 Oct 2026 16:58:40 +0200	[thread overview]
Message-ID: <asukIMajFpbzx3PH@strlen.de> (raw)
In-Reply-To: <9c51ddfce3069d585b6c9b87e7e1bf3a3b0a2761.1791688819.git.zihanx@nebusec.ai>

Zihan Xi <zihanx@nebusec.ai> wrote:
> nf_dup_ipv4() and nf_dup_ipv6() set current->in_nf_duplicate while the
> cloned skb traverses output hooks. NFQUEUE can retain the clone and return
> NF_STOLEN while that traversal is in progress. Once ip_local_out() or
> ip6_local_out() returns, the task-local guard is cleared. NF_ACCEPT then
> resumes hook traversal after the queued hook in the verdict task, where
> the guard is no longer set. A later dup rule can clone the packet again,
> causing repeated packet processing and resource consumption.

I find this waaaaay too complex.

>  include/linux/sched.h            |  2 ++
>  include/net/netfilter/nf_queue.h |  1 +
>  net/ipv4/netfilter/ip_tables.c   |  5 ++++-
>  net/ipv4/netfilter/nf_dup_ipv4.c |  5 ++++-
>  net/ipv6/netfilter/ip6_tables.c  |  5 ++++-
>  net/ipv6/netfilter/nf_dup_ipv6.c |  6 +++++-
>  net/netfilter/nf_dup_netdev.c    | 13 ++++++++++++-
>  net/netfilter/nf_queue.c         |  4 ++++
>  net/netfilter/nfnetlink_queue.c  |  8 +++++++-
>  9 files changed, 43 insertions(+), 6 deletions(-)

Holy cow...  This is waaaay to large/complex, sorry.

>  	unsigned			in_nf_duplicate:1;
> +	unsigned			in_nf_dup_netdev:1;	/* netdev dup output is active */
> +	unsigned			in_nf_duplicate_reinject:1;	/* NFQUEUE resume */

I don't understand this.

>  	struct nf_hook_state	state;
> +	bool			in_nf_duplicate;	/* skb is on a duplication path */

OK.

> diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
> index 809441ced..7c2027ef8 100644
> --- a/net/ipv4/netfilter/ip_tables.c
> +++ b/net/ipv4/netfilter/ip_tables.c
> @@ -14,6 +14,7 @@
>  #include <linux/vmalloc.h>
>  #include <linux/netdevice.h>
>  #include <linux/module.h>
> +#include <linux/preempt.h>
>  #include <net/ip.h>
>  #include <net/compat.h>
>  #include <linux/uaccess.h>
> @@ -270,7 +271,9 @@ ipt_do_table(void *priv,
>  	 * but it is no problem since absolute verdict is issued by these.
>  	 */
>  	if (static_key_false(&xt_tee_enabled))
> -		jumpstack += private->stacksize * current->in_nf_duplicate;
> +		jumpstack += private->stacksize *
> +			     (current->in_nf_duplicate ||
> +			      (in_task() && current->in_nf_duplicate_reinject));

?  Aside from me not getting the need for in_nf_duplicate_reinject, why
is the in_task() needed but for in_nf_duplicate its not needed...?

As those are bits they even reside on same address...

>  {
>  	struct iphdr *iph;
> +	bool in_nf_duplicate_reinject = in_task() &&
> +					current->in_nf_duplicate_reinject;

Same question.

>  #include <linux/slab.h>
>  #include <linux/init.h>
>  #include <linux/module.h>
> @@ -219,6 +220,9 @@ static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
>  	*entry = (struct nf_queue_entry) {
>  		.skb	= skb,
>  		.skb_dev = skb->dev,
> +		.in_nf_duplicate = current->in_nf_duplicate ||
> +				  (in_task() && current->in_nf_duplicate_reinject) ||
> +				  current->in_nf_dup_netdev,

Why not simply

	.in_nf_dup_netdev = current->in_nf_duplicate ?


> +	bool old_in_nf_duplicate_reinject;
>  
>  	net = entry->state.net;
>  	pf = entry->state.pf;
> +	old_in_nf_duplicate_reinject = current->in_nf_duplicate_reinject;
> +	current->in_nf_duplicate_reinject |= entry->in_nf_duplicate;

Don't understand the need to store the old current state here.
Nor the "|=".

> @@ -378,7 +382,7 @@ static void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
>  	if (!hooks || i >= hooks->num_hook_entries) {
>  		kfree_skb_reason(skb, SKB_DROP_REASON_NETFILTER_DROP);
>  		nf_queue_entry_free(entry);
> -		return;
> +		goto out;
>  	}
>  
>  	hook_entry = &hooks->hooks[i];
> @@ -417,6 +421,8 @@ static void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
>  	}
>  
>  	nf_queue_entry_free(entry);
> +out:
> +	current->in_nf_duplicate_reinject = old_in_nf_duplicate_reinject;

... nor this.  AFAICS the only change we really need is to move the
local_bh_disable/enable pair to cover the hook iteration is well in
nf_reinject() to avoid BH interference clearing current->in_nf_duplicate
resp. acting on wron rect "current" task state.

I think we either need to find a way to make this patch way simpler or
go back to the "no duplication/TEE in userns".  As I already wrote
earlier, I see no use case for dup/TEE is namespaces, except for CI
maybe.

Also, I think there are many many other ways to make kernel eat lots of
RAM, so I don't think there is any need to ftarget nf tree either.

Sorry, I know you've been spinning on this for a while, but this is too
hard for me to comprehend.

  reply	other threads:[~2026-10-11 14:58 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-11 11:28 [PATCH nf v7 0/1] " Zihan Xi
2026-10-11 11:28 ` [PATCH nf v7 1/1] " Zihan Xi
2026-10-11 14:58   ` Florian Westphal [this message]
2026-10-12  1:34     ` zihan xi

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=asukIMajFpbzx3PH@strlen.de \
    --to=fw@strlen.de \
    --cc=bsegall@google.com \
    --cc=davem@davemloft.net \
    --cc=dietmar.eggemann@arm.com \
    --cc=edumazet@kernel.org \
    --cc=horms@kernel.org \
    --cc=jengelh@medozas.de \
    --cc=juri.lelli@redhat.com \
    --cc=kaber@trash.net \
    --cc=kprateek.nayak@amd.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lukas@wunner.de \
    --cc=mgorman@suse.de \
    --cc=mingo@redhat.com \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=pablo@netfilter.org \
    --cc=peterz@infradead.org \
    --cc=phil@nwl.cc \
    --cc=root@tr0jan.top \
    --cc=rostedt@goodmis.org \
    --cc=vega@nebusec.ai \
    --cc=vincent.guittot@linaro.org \
    --cc=vschneid@redhat.com \
    --cc=zihanx@nebusec.ai \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®