* [BUG] netfilter: unbounded nftables reject recursion on inet OUTPUT
@ 2026-09-21 6:13 Yu Junzhe
2026-09-21 9:28 ` Florian Westphal
0 siblings, 1 reply; 3+ messages in thread
From: Yu Junzhe @ 2026-09-21 6:13 UTC (permalink / raw)
To: Pablo Neira Ayuso, Florian Westphal; +Cc: netfilter-devel, coreteam
Hello,
I am reporting unbounded hook recursion on inet OUTPUT: an nftables
output chain that does a TCP flags payload-set followed by
"reject with tcp reset" makes nf_send_reset() re-enter the same
NF_INET_LOCAL_OUT hook until the kernel stack is exhausted.
Summary
=======
nft_reject_inet_eval() calls nf_send_reset(), which builds a RST skb
and transmits it with ip_local_out(). That walks the same OUTPUT
hooks again.
The only loop guard in nf_reject_ip_tcphdr_get() is the packet-content
check:
/* No RST for RST. */
if (oth->rst)
return NULL;
If a payload-set runs first and rewrites the TCP flags byte to SYN,
that guard never sees RST, so another RST is generated. The cycle is:
nft_do_chain
-> nft_reject_inet_eval
-> nf_send_reset
-> ip_local_out
-> nf_hook_slow
-> nft_do_chain
nft_reject_inet_validate() allows NF_INET_LOCAL_OUT.
This is a legal nftables configuration (payload set + reject with tcp
reset on an output base chain), including with CAP_NET_ADMIN in a
network namespace. I do not claim privilege escalation; the result we
see is local DoS (stack overflow, then panic).
I am aware of the existing recursion bound on the netdev fwd/neigh
xmit path. nf_send_reset() via ip_local_out() does not use it.
Affected
========
- Confirmed on Linux 6.6.144, KASAN guest (CONFIG_VMAP_STACK not set).
On that config the overflowing SP walks into adjacent slab; KASAN
first reports a write in __unwind_start while tracking __alloc_skb
from the next RST. Those frames are collateral. The defect is
unbounded hook recursion.
- Source review of torvalds/linux master as of 2026-09-21:
nf_send_reset() still calls ip_local_out(); the only loop guard is
still if (oth->rst); nft_reject_inet_validate() still allows
NF_INET_LOCAL_OUT. The PoC was not re-run on tip.
- Files: net/ipv4/netfilter/nf_reject_ipv4.c,
net/netfilter/nft_reject_inet.c
- Config: CONFIG_NF_TABLES=y, CONFIG_NF_TABLES_INET=y,
CONFIG_NFT_REJECT_INET=y (KASAN used for the report)
Ruleset that triggers it
========================
table inet t {
chain out {
type filter hook output priority 0;
tcp flags set syn
reject with tcp reset
}
}
Then connect() to 127.0.0.1 in that netns (a SYN on lo).
Crash excerpt (minimized PoC, Linux 6.6.144 KASAN)
==================================================
BUG: KASAN: use-after-free in __unwind_start+0x36/0x840
Write of size 112 at addr ff1100000659ff88 by task repro/221
CPU: 1 PID: 221 Comm: repro Not tainted 6.6.144 #1
Call Trace:
__alloc_skb
nf_send_reset
nft_reject_inet_eval
nft_do_chain
nft_do_chain_inet
nf_hook_slow
__ip_local_out
ip_local_out
nf_send_reset
nft_reject_inet_eval
nft_do_chain
... (repeats until stack exhaustion; then GPF / panic)
Reproducer
==========
A self-contained C PoC (raw NETLINK_NETFILTER batch, no nft binary)
and a Docker/QEMU package are available on request. VM-only: the
program overflows the guest kernel stack. Please do not run it on a
host kernel.
I am happy to test patches or send the reproducer.
Thanks,
Yu Junzhe
FuzzAnything <fuzzanything@gmail.com>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [BUG] netfilter: unbounded nftables reject recursion on inet OUTPUT
2026-09-21 6:13 [BUG] netfilter: unbounded nftables reject recursion on inet OUTPUT Yu Junzhe
@ 2026-09-21 9:28 ` Florian Westphal
2026-09-21 10:32 ` Fernando Fernandez Mancera
0 siblings, 1 reply; 3+ messages in thread
From: Florian Westphal @ 2026-09-21 9:28 UTC (permalink / raw)
To: Yu Junzhe; +Cc: Pablo Neira Ayuso, netfilter-devel, coreteam
Yu Junzhe <junzheyu1@gmail.com> wrote:
> I am reporting unbounded hook recursion on inet OUTPUT: an nftables
> output chain that does a TCP flags payload-set followed by
> "reject with tcp reset" makes nf_send_reset() re-enter the same
> NF_INET_LOCAL_OUT hook until the kernel stack is exhausted.
>
> Summary
> =======
>
> nft_reject_inet_eval() calls nf_send_reset(), which builds a RST skb
> and transmits it with ip_local_out(). That walks the same OUTPUT
> hooks again.
Can you send us a patch for this?
> The only loop guard in nf_reject_ip_tcphdr_get() is the packet-content
> check:
>
> /* No RST for RST. */
> if (oth->rst)
> return NULL;
>
> If a payload-set runs first and rewrites the TCP flags byte to SYN,
> that guard never sees RST, so another RST is generated. The cycle is:
>
> nft_do_chain
> -> nft_reject_inet_eval
> -> nf_send_reset
> -> ip_local_out
> -> nf_hook_slow
> -> nft_do_chain
>
> nft_reject_inet_validate() allows NF_INET_LOCAL_OUT.
>
> This is a legal nftables configuration (payload set + reject with tcp
> reset on an output base chain), including with CAP_NET_ADMIN in a
> network namespace. I do not claim privilege escalation; the result we
> see is local DoS (stack overflow, then panic).
>
> I am aware of the existing recursion bound on the netdev fwd/neigh
> xmit path. nf_send_reset() via ip_local_out() does not use it.
Tell your coding assistant to move it to include/net/netfilter/nf_recursion.h,
add that include to the existing users, then extend all reset functions
(icmp and tcp reset) to use those helpers.
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [BUG] netfilter: unbounded nftables reject recursion on inet OUTPUT
2026-09-21 9:28 ` Florian Westphal
@ 2026-09-21 10:32 ` Fernando Fernandez Mancera
0 siblings, 0 replies; 3+ messages in thread
From: Fernando Fernandez Mancera @ 2026-09-21 10:32 UTC (permalink / raw)
To: Florian Westphal, Yu Junzhe; +Cc: Pablo Neira Ayuso, netfilter-devel, coreteam
On 9/21/26 11:28 AM, Florian Westphal wrote:
> Yu Junzhe <junzheyu1@gmail.com> wrote:
>> I am reporting unbounded hook recursion on inet OUTPUT: an nftables
>> output chain that does a TCP flags payload-set followed by
>> "reject with tcp reset" makes nf_send_reset() re-enter the same
>> NF_INET_LOCAL_OUT hook until the kernel stack is exhausted.
>>
>> Summary
>> =======
>>
>> nft_reject_inet_eval() calls nf_send_reset(), which builds a RST skb
>> and transmits it with ip_local_out(). That walks the same OUTPUT
>> hooks again.
>
> Can you send us a patch for this?
Hi! Sorry, I have a patch for this already. So no need to do overlap
work, will send it shortly.
Thanks for the report, I manage to trigger this easily.
>
>> The only loop guard in nf_reject_ip_tcphdr_get() is the packet-content
>> check:
>>
>> /* No RST for RST. */
>> if (oth->rst)
>> return NULL;
>>
>> If a payload-set runs first and rewrites the TCP flags byte to SYN,
>> that guard never sees RST, so another RST is generated. The cycle is:
>>
>> nft_do_chain
>> -> nft_reject_inet_eval
>> -> nf_send_reset
>> -> ip_local_out
>> -> nf_hook_slow
>> -> nft_do_chain
>>
>> nft_reject_inet_validate() allows NF_INET_LOCAL_OUT.
>>
>> This is a legal nftables configuration (payload set + reject with tcp
>> reset on an output base chain), including with CAP_NET_ADMIN in a
>> network namespace. I do not claim privilege escalation; the result we
>> see is local DoS (stack overflow, then panic).
>>
>> I am aware of the existing recursion bound on the netdev fwd/neigh
>> xmit path. nf_send_reset() via ip_local_out() does not use it.
>
> Tell your coding assistant to move it to include/net/netfilter/nf_recursion.h,
> add that include to the existing users, then extend all reset functions
> (icmp and tcp reset) to use those helpers.
>
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-21 10:33 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-21 6:13 [BUG] netfilter: unbounded nftables reject recursion on inet OUTPUT Yu Junzhe
2026-09-21 9:28 ` Florian Westphal
2026-09-21 10:32 ` Fernando Fernandez Mancera
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.