WARNING in xt_dnat_target_v2

Status: upstream: reported C repro on 2026/11/05 21:17
Subsystems: netfilter
Labels: prio:high
[Documentation on labels]
Reported-by: syzbot+2e819f6641708bb6ab9d@syzkaller.appspotmail.com
First crash: 4d12h, last: 2d00h
✨ AI-generated root cause summary:

When an incoming TCP packet is rejected by an nftables reject rule (nft_reject_ipv4_eval), nf_send_reset() creates a TCP RST reply and attaches the original packet's conntrack entry via nf_ct_attach(), marking its ctinfo as IP_CT_RELATED_REPLY. As the RST packet traverses the NF_INET_LOCAL_OUT hook, nf_nat_ipv4_fn() passes it to nf_nat_inet_fn() because it is not an ICMP packet. In nf_nat_inet_fn(), IP_CT_RELATED_REPLY falls through to evaluation of uninitialized NAT mappings, executing rules in the LOCAL_OUT NAT chain. If the chain includes an xtables DNAT target (evaluated via nft_compat), xt_dnat_target_v2() triggers WARN_ON because its assertion checks only for IP_CT_NEW or IP_CT_RELATED, omitting IP_CT_RELATED_REPLY.

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit cb284914e749:

Root Cause Analysis

Involved Functions, Structs, and Files


Triggering Mechanism and Invariant Violation

  1. Triggering Packet Rejection: An incoming IPv4 TCP packet (oldskb) arrives in the original direction (IP_CT_DIR_ORIGINAL) with an associated connection tracking entry (ctinfo == IP_CT_NEW). If the packet is matched by an nftables filter rule before PREROUTING NAT completes destination NAT initialization (or if no PREROUTING NAT table/hook is registered), IPS_DST_NAT_DONE is not set on the conntrack entry. The rule evaluates nft_reject_ipv4_eval(), which calls nf_send_reset().

  2. Attachment of Related Conntrack Info: nf_send_reset() synthesizes a TCP reset packet (nskb) and attaches the conntrack entry of the rejected packet by calling nf_ct_attach(nskb, oldskb). This invokes nf_conntrack_attach(), which inspects the original packet's direction:

    ct = nf_ct_get(skb, &ctinfo);
    if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL)
        ctinfo = IP_CT_RELATED_REPLY;
    else
        ctinfo = IP_CT_RELATED;
    
    nf_ct_set(nskb, ct, ctinfo);
    nf_conntrack_get(skb_nfct(nskb));
    

    Because oldskb was in IP_CT_DIR_ORIGINAL, the newly generated reset packet nskb is tagged with ctinfo = IP_CT_RELATED_REPLY.

  3. Traversal of Local Output Hooks: nf_send_reset() then transmits the packet via ip_local_out(), entering the NF_INET_LOCAL_OUT netfilter hook. The hook calls nf_nat_ipv4_local_fn() $\rightarrow$ nf_nat_ipv4_fn(). In nf_nat_ipv4_fn():

    if (ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY) {
        if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
            if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, state->hook))
                return NF_DROP;
            else
                return NF_ACCEPT;
        }
    }
    
    return nf_nat_inet_fn(priv, skb, state);
    

    Because nskb is a TCP packet (IPPROTO_TCP) and not an ICMP packet, it bypasses ICMP translation and enters nf_nat_inet_fn().

  4. NAT Chain Evaluation: In nf_nat_inet_fn():

    • The hook is NF_INET_LOCAL_OUT, so maniptype is NF_NAT_MANIP_DST.
    • For ctinfo == IP_CT_RELATED_REPLY, the switch statement falls through to case IP_CT_NEW:
      switch (ctinfo) {
      case IP_CT_RELATED:
      case IP_CT_RELATED_REPLY:
          /* Only ICMPs can be IP_CT_IS_REPLY. Fallthrough */
      case IP_CT_NEW:
          if (!nf_nat_initialized(ct, maniptype)) {
              ...
              for (i = 0; i < e->num_hook_entries; i++) {
                  ret = e->hooks[i].hook(e->hooks[i].priv, skb, state);
      
    • Because ct has not yet had NF_NAT_MANIP_DST initialized (!nf_nat_initialized(ct, NF_NAT_MANIP_DST) is true), nf_nat_inet_fn() executes the rules on the registered LOCAL_OUT NAT chain (nft_nat_do_chain).
  5. Inconsistent State Assertion in xt_dnat_target: The chain matches and evaluates an xtables DNAT target via nft_compat (nft_target_eval_xt()), invoking xt_dnat_target_v2(). Inside xt_dnat_target_v2():

    ct = nf_ct_get(skb, &ctinfo);
    WARN_ON(!(ct != NULL &&
             (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)));
    

    While xt_snat_target_v0/v1/v2() explicitly permit ctinfo == IP_CT_RELATED_REPLY, the DNAT targets (xt_dnat_target_v0/v1/v2()) only expect IP_CT_NEW or IP_CT_RELATED. Because ctinfo is IP_CT_RELATED_REPLY, the assertion check fails, producing the reported warning:

    !(ct != NULL && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED))
    WARNING: net/netfilter/xt_nat.c:154 at xt_dnat_target_v2+0x97/0xc0
    
▶ ▼ ✨ AI Jobs (2)
ID Workflow Result Correct Bug Created Started Finished Revision
d311b2df repro-c Reproduced: ✅ ❓ WARNING in xt_dnat_target_v2 2026/11/04 00:00 2026/11/04 00:00 2026/11/04 04:23 d02c3949
67f9d382 assessment-security DenialOfService: ❌ Exploitable: ❌ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ❌ NetworkNeedsSetup: ✅ NetworkTrigger: ✅ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ✅ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ WARNING in xt_dnat_target_v2 2026/11/01 21:10 2026/11/01 21:10 2026/11/01 21:59 25a0ae84
▶ ▼ Discussions (2)
Title Replies (including bot) Last reply
[syzbot] [netfilter?] WARNING in xt_dnat_target_v2 0 (3) 2026/11/06 08:18
[PATCH nf] netfilter: xt_nat: allow IP_CT_RELATED_REPLY in DNAT targets 2 (2) 2026/11/06 07:32
▶ ▼ Last patch testing requests (3)
Created Duration User Patch Repo Result
2026/11/06 07:28 21m xujiahao@cmss.chinamobile.com patch git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git master error job log
2026/11/04 07:55 29m retest repro git://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git 787268686c64be60023743b527645a907e38ccca report job log log
2026/11/06 08:18 xujiahao@cmss.chinamobile.com patch upstream running

Sample crash report:
------------[ cut here ]------------
!(ct != NULL && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED))
WARNING: net/netfilter/xt_nat.c:154 at xt_dnat_target_v2+0x97/0xc0 net/netfilter/xt_nat.c:153, CPU#0: syz-executor145/6031
Modules linked in:
CPU: 0 UID: 0 PID: 6031 Comm: syz-executor145 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/15/2026
RIP: 0010:xt_dnat_target_v2+0x97/0xc0 net/netfilter/xt_nat.c:153
Code: 74 22 41 83 e7 07 41 83 c7 fd bf fe ff ff ff 44 89 fe e8 ac 1e 90 f7 41 83 ff fe 72 25 e8 91 19 90 f7 eb 09 e8 8a 19 90 f7 90 <0f> 0b 90 4c 89 f7 48 89 de ba 01 00 00 00 5b 41 5e 41 5f e9 21 17
RSP: 0018:ffffc90000006de0 EFLAGS: 00010246
RAX: ffffffff8a38fc64 RBX: ffff8880614cd418 RCX: ffff88807c979f40
RDX: 0000000000000100 RSI: 0000000000000100 RDI: 00000000fffffffe
RBP: ffffc90000006ed0 R08: 0000000000000100 R09: 0000000000000000
R10: ffffc900000070b0 R11: ffffffff8a38fbb0 R12: ffffffff919e5438
R13: 1ffff92000000dc4 R14: ffff8880247c8900 R15: 0000000000000001
FS:  0000555563505400(0000) GS:ffff88812361d000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe16ab8280 CR3: 0000000034dea000 CR4: 00000000003526f0
Call Trace:
 <IRQ>
 nft_target_eval_xt+0x1ad/0x2b0 net/netfilter/nft_compat.c:100
 expr_call_ops_eval net/netfilter/nf_tables_core.c:237 [inline]
 nft_do_chain+0x48d/0x1b10 net/netfilter/nf_tables_core.c:285
 nft_nat_do_chain+0x360/0x4b0 net/netfilter/nft_chain_nat.c:32
 nf_nat_inet_fn+0x5de/0xb50 net/netfilter/nf_nat_core.c:931
 nf_nat_ipv4_fn net/netfilter/nf_nat_proto.c:583 [inline]
 nf_nat_ipv4_local_fn+0x18c/0x570 net/netfilter/nf_nat_proto.c:738
 nf_hook_entry_hookfn include/linux/netfilter.h:165 [inline]
 nf_hook_slow+0xc5/0x220 net/netfilter/core.c:633
 nf_hook+0x22a/0x3a0 include/linux/netfilter.h:280
 __ip_local_out+0x558/0x6a0 net/ipv4/ip_output.c:120
 ip_local_out+0x2a/0x190 net/ipv4/ip_output.c:129
 nf_send_reset+0xd8d/0x1530 net/ipv4/netfilter/nf_reject_ipv4.c:339
 nft_reject_ipv4_eval+0xf6/0x230 net/ipv4/netfilter/nft_reject_ipv4.c:30
 expr_call_ops_eval net/netfilter/nf_tables_core.c:237 [inline]
 nft_do_chain+0x48d/0x1b10 net/netfilter/nf_tables_core.c:285
 nft_do_chain_ipv4+0x195/0x290 net/netfilter/nft_chain_filter.c:24
 nf_hook_entry_hookfn include/linux/netfilter.h:165 [inline]
 nf_hook_slow+0xc5/0x220 net/netfilter/core.c:633
 nf_hook include/linux/netfilter.h:280 [inline]
 NF_HOOK+0x21f/0x3c0 include/linux/netfilter.h:323
 __netif_receive_skb_one_core net/core/dev.c:6433 [inline]
 __netif_receive_skb net/core/dev.c:6546 [inline]
 process_backlog+0xa6b/0x18b0 net/core/dev.c:6897
 __napi_poll+0xaa/0x330 net/core/dev.c:7977
 napi_poll net/core/dev.c:8040 [inline]
 net_rx_action+0x61d/0xf50 net/core/dev.c:8197
 handle_softirqs+0x265/0x8d0 kernel/softirq.c:669
 do_softirq+0x77/0xd0 kernel/softirq.c:569
 </IRQ>
 <TASK>
 __local_bh_enable_ip+0x100/0x140 kernel/softirq.c:473
 local_bh_enable include/linux/bottom_half.h:33 [inline]
 rcu_read_unlock_bh include/linux/rcupdate.h:923 [inline]
 __dev_queue_xmit+0x1efb/0x3920 net/core/dev.c:5121
 neigh_output include/net/neighbour.h:554 [inline]
 ip_finish_output2+0xc4b/0x10b0 net/ipv4/ip_output.c:236
 NF_HOOK_COND include/linux/netfilter.h:314 [inline]
 ip_output+0x29f/0x450 net/ipv4/ip_output.c:437
 __ip_queue_xmit+0x10df/0x1b10 net/ipv4/ip_output.c:533
 __tcp_transmit_skb+0x2d06/0x47b0 net/ipv4/tcp_output.c:1773
 tcp_transmit_skb net/ipv4/tcp_output.c:1790 [inline]
 tcp_connect+0x1a7e/0x4990 net/ipv4/tcp_output.c:4451
 tcp_v4_connect+0x1264/0x1b20 net/ipv4/tcp_ipv4.c:348
 __inet_stream_connect+0x28c/0xf50 net/ipv4/af_inet.c:684
 inet_stream_connect+0x66/0xa0 net/ipv4/af_inet.c:755
 connect_socket net/socket.c:2143 [inline]
 __sys_connect_file net/socket.c:2168 [inline]
 __sys_connect+0x316/0x450 net/socket.c:2185
 __do_sys_connect net/socket.c:2191 [inline]
 __se_sys_connect net/socket.c:2188 [inline]
 __x64_sys_connect+0x7a/0x90 net/socket.c:2188
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f81c24b2ff7
Code: 48 89 fa 4c 89 df e8 98 1d 00 00 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b> c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
RSP: 002b:00007ffe16aca280 EFLAGS: 00000202 ORIG_RAX: 000000000000002a
RAX: ffffffffffffffda RBX: 0000555563505400 RCX: 00007f81c24b2ff7
RDX: 0000000000000010 RSI: 00007ffe16aca300 RDI: 0000000000000005
RBP: 0000000000000001 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000202 R12: 00000000d2040002
R13: 00007f81c24eed50 R14: 00007ffe16aca300 R15: 00007f81c24ec06c
 </TASK>

Crashes (2):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/11/04 08:25 git://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git 787268686c64 25a0ae84 .config console log report C ci-upstream-net-this-kasan-gce WARNING in xt_dnat_target_v2
2026/11/01 21:10 net cb284914e749 25a0ae84 .config console log report info [disk image] [vmlinux] [kernel image] ci-upstream-net-this-kasan-gce WARNING in xt_dnat_target_v2
* Struck through repros no longer work on HEAD.