Subject: Re: [PATCH] netfilter: iptable_nat: fix null-ptr-deref in ipt_nat_register_lookups

From: Florian Westphal <[email-masked]>
Date: Sun, 4 Jan 2026 16:37:33 +0100

王志 <[email-masked]> wrote:
> Dear Developers,
> 
> I am reporting a null-pointer dereference detected by Syzkaller on Linux 6.18.0. The issue occurs in the netfilter subsystem during concurrent network namespace creation and module loading.
> 1. Analysis
> The crash is triggered by a race condition between the registration of the iptable_nat template and the initialization of per-net private data.
> In ipt_nat_register_lookups(), the driver attempts to retrieve its private data using net_generic(net, iptable_nat_net_id). However, if the module is being loaded while a new network namespace is being initialized, net_generic can return NULL because the storage for this specific module ID has not yet been allocated or linked for the target namespace.
> The code currently proceeds without checking if xt_nat_net is valid. When it subsequently attempts to store the ops pointers: xt_nat_net->nf_nat_ops = ops; It results in a null-pointer dereference (GPF), specifically at an offset (e.g., 0x18) from the NULL base.

This needs a better description on the sequence of events that leads to
this bug.  I also don't understand the bit about the 0x18 offset.

struct iptable_nat_pernet {
        struct nf_hook_ops *nf_nat_ops;
};

... so where would 0x18 come from?

If this is AI generated, please don't do that :-|

> 2. Proposed Fix
> The fix involves adding a NULL check for the pointer returned by net_generic(). If it returns NULL, the registration should be aborted with -ENOMEM to prevent the kernel crash.
> 
> 3. Patch
> Diff
> --- a/net/ipv4/netfilter/iptable_nat.c
> +++ b/net/ipv4/netfilter/iptable_nat.c
> @@ -66,6 +66,9 @@ static int ipt_nat_register_lookups(struct net *net)
>  	int i, ret;
>  
>  	xt_nat_net = net_generic(net, iptable_nat_net_id);
> +	if (!xt_nat_net)
> +		return -ENOMEM;
> +

ip6t_nat_register_lookups() needs the same check, assuming this report
is correct.

> 4. Bug Trace Highlights
> KASAN: null-ptr-deref in range [0x0000000000000018-0x000000000000001f]
> RIP: 0010:ipt_nat_register_lookups+0xf6/0x1e0
> Code: 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 
> Call Trace:
>  <TASK>
>  xt_find_table_lock+0x20a/0x2f0 home/wmy/Fuzzer/third_tool/linux-6.7-defconfig/net/netfilter/x_tables.c:1259
>  xt_request_find_table_lock+0x2b/0xc0 home/wmy/Fuzzer/third_tool/linux-6.7-defconfig/net/netfilter/x_tables.c:1284
>  get_info+0xec/0x330 home/wmy/Fuzzer/third_tool/linux-6.7-defconfig/net/ipv4/netfilter/ip_tables.c:963
>  do_ipt_get_ctl+0x134/0xa60 home/wmy/Fuzzer/third_tool/linux-6.7-defconfig/net/ipv4/netfilter/ip_tables.c:1651
>  nf_getsockopt+0x6a/0xa0 home/wmy/Fuzzer/third_tool/linux-6.7-defconfig/net/netfilter/nf_sockopt.c:116
>  ip_getsockopt+0x190/0x1f0 home/wmy/Fuzzer/third_tool/linux-6.7-defconfig/net/ipv4/ip_sockglue.c:1781
>  tcp_getsockopt+0x7f/0xd0 home/wmy/Fuzzer/third_tool/linux-6.7-defconfig/net/ipv4/tcp.c:4340
>  do_sock_getsockopt+0x20b/0x280 home/wmy/Fuzzer/third_tool/linux-6.7-defconfig/net/socket.c:2377
>  __sys_getsockopt+0x115/0x1b0 home/wmy/Fuzzer/third_tool/linux-6.7-defconfig/net/socket.c:2406
>  __do_sys_getsockopt home/wmy/Fuzzer/third_tool/linux-6.7-defconfig/net/socket.c:2416 [inline]
>  __se_sys_getsockopt home/wmy/Fuzzer/third_tool/linux-6.7-defconfig/net/socket.c:2413 [inline]
>  __x64_sys_getsockopt+0x64/0x80 home/wmy/Fuzzer/third_tool/linux-6.7-defconfig/net/socket.c:2413
>  do_syscall_x64 home/wmy/Fuzzer/third_tool/linux-6.7-defconfig/arch/x86/entry/common.c:51 [inline]
>  do_syscall_64+0x46/0xf0 home/wmy/Fuzzer/third_tool/linux-6.7-defconfig/arch/x86/entry/common.c:82
>  entry_SYSCALL_64_after_hwframe+0x6f/0x77
>  </TASK>

Please describe the sequence of events that lead to this bug.

1. xt_find_table_lock() found the nat table in xt_templates[] list.
2. BUT iptable_nat_init calls xt_register_template() AFTER
   register_pernet_subsys() returns; not before.

How does xt_find_table_lock() observe the nat table but the associated
net generic slot is NULL?

Under what condition will register_pernet_subsys() return 0 but leave
net->gen->ptr[iptable_nat_net_id] == NULL?

5830aa863981 ("netfilter: iptables: Fix null-ptr-deref in iptable_nat_table_init().")
changed the ordering to avoid this exact problem, so I wonder what
was missing.