* [PATCH net] netfilter: ctnetlink: fix use-after-free in ctnetlink_dump_exp_ct()
@ 2026-03-07 17:21 Hyunwoo Kim
0 siblings, 0 replies; only message in thread
From: Hyunwoo Kim @ 2026-03-07 17:21 UTC (permalink / raw)
To: pablo, fw, phil, davem, edumazet, kuba, pabeni, horms
Cc: netfilter-devel, coreteam, netdev, imv4bel
ctnetlink_dump_exp_ct() stores a conntrack pointer in cb->data for the
netlink dump callback ctnetlink_exp_ct_dump_table(), but drops the
conntrack reference immediately after netlink_dump_start(). When the
dump spans multiple rounds, the second recvmsg() triggers the dump
callback which dereferences the now-freed conntrack via nfct_help(ct),
leading to a use-after-free on ct->ext.
The bug is that the netlink_dump_control has no .start or .done
callbacks to manage the conntrack reference across dump rounds. Other
dump functions in the same file (e.g. ctnetlink_get_conntrack) properly
use .start/.done callbacks for this purpose.
Fix this by adding .start and .done callbacks that hold and release the
conntrack reference for the duration of the dump, and move the
nfct_help() call after the cb->args[0] early-return check in the dump
callback to avoid dereferencing ct->ext unnecessarily.
KASAN report:
[ 3.484270] BUG: KASAN: slab-use-after-free in ctnetlink_exp_ct_dump_table+0x4f/0x2e0
[ 3.484508] Read of size 8 at addr ffff88810597ebf0 by task ctnetlink_poc/133
[ 3.484717]
[ 3.484772] CPU: 1 UID: 0 PID: 133 Comm: ctnetlink_poc Not tainted 7.0.0-rc2+ #3 PREEMPTLAZY
[ 3.484775] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 3.484778] Call Trace:
[ 3.484782] <TASK>
[ 3.484785] dump_stack_lvl+0x64/0x80
[ 3.484797] print_report+0xce/0x660
[ 3.484804] ? __pfx__raw_spin_lock_irqsave+0x10/0x10
[ 3.484811] ? __kmalloc_large_node_noprof+0x1f/0xc0
[ 3.484818] ? __virt_addr_valid+0xef/0x1a0
[ 3.484825] ? ctnetlink_exp_ct_dump_table+0x4f/0x2e0
[ 3.484827] kasan_report+0xce/0x100
[ 3.484829] ? ctnetlink_exp_ct_dump_table+0x4f/0x2e0
[ 3.484833] ctnetlink_exp_ct_dump_table+0x4f/0x2e0
[ 3.484836] netlink_dump+0x333/0x880
[ 3.484841] ? __pfx_netlink_dump+0x10/0x10
[ 3.484844] ? netlink_recvmsg+0x27c/0x4b0
[ 3.484847] ? kmem_cache_free+0x100/0x440
[ 3.484849] ? netlink_recvmsg+0x27c/0x4b0
[ 3.484851] netlink_recvmsg+0x3e2/0x4b0
[ 3.484855] ? aa_sk_perm+0x184/0x450
[ 3.484862] ? __pfx_netlink_recvmsg+0x10/0x10
[ 3.484864] ? __pfx_aa_sk_perm+0x10/0x10
[ 3.484866] ? mutex_unlock+0x80/0xd0
[ 3.484870] ? __pfx_netlink_recvmsg+0x10/0x10
[ 3.484873] sock_recvmsg+0xde/0xf0
[ 3.484880] __sys_recvfrom+0x150/0x200
[ 3.484882] ? __pfx___sys_recvfrom+0x10/0x10
[ 3.484885] ? ksys_write+0xe1/0x160
[ 3.484889] ? __pfx_ksys_write+0x10/0x10
[ 3.484891] __x64_sys_recvfrom+0x76/0x90
[ 3.484893] do_syscall_64+0xc3/0x6e0
[ 3.484898] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 3.484903] RIP: 0033:0x42366d
[ 3.484907] Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 80 3d fd 19 09 00 00 41 89 ca 74 20 45 31 c9 45 31 c0 b8 2d 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 6b c3 66 2e 0f 1f 84 00 00 00 00 00 55 48 89
[ 3.484908] RSP: 002b:00007fffb7e89be8 EFLAGS: 00000246 ORIG_RAX: 000000000000002d
[ 3.484915] RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 000000000042366d
[ 3.484916] RDX: 0000000000004000 RSI: 00007fffb7e8ac30 RDI: 0000000000000003
[ 3.484920] RBP: 00007fffb7e8ec50 R08: 0000000000000000 R09: 0000000000000000
[ 3.484921] R10: 0000000000000000 R11: 0000000000000246 R12: 00007fffb7e8ed68
[ 3.484922] R13: 00007fffb7e8ed78 R14: 00000000004af868 R15: 0000000000000001
[ 3.484924] </TASK>
[ 3.484925]
[ 3.491086] Allocated by task 133:
[ 3.491190] kasan_save_stack+0x33/0x60
[ 3.491308] kasan_save_track+0x14/0x30
[ 3.491425] __kasan_slab_alloc+0x6e/0x70
[ 3.491545] kmem_cache_alloc_noprof+0x134/0x440
[ 3.491683] __nf_conntrack_alloc+0xa8/0x2b0
[ 3.491815] ctnetlink_create_conntrack+0xa1/0x900
[ 3.491959] ctnetlink_new_conntrack+0x3cf/0x7d0
[ 3.492097] nfnetlink_rcv_msg+0x48e/0x510
[ 3.492223] netlink_rcv_skb+0xc9/0x1f0
[ 3.492339] nfnetlink_rcv+0xdb/0x220
[ 3.492450] netlink_unicast+0x3ec/0x590
[ 3.492568] netlink_sendmsg+0x397/0x690
[ 3.492687] ____sys_sendmsg+0x538/0x550
[ 3.492807] ___sys_sendmsg+0xfc/0x170
[ 3.492921] __sys_sendmsg+0xf4/0x180
[ 3.493032] do_syscall_64+0xc3/0x6e0
[ 3.493143] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 3.493293]
[ 3.493344] Freed by task 0:
[ 3.493433] kasan_save_stack+0x33/0x60
[ 3.493549] kasan_save_track+0x14/0x30
[ 3.493666] kasan_save_free_info+0x3b/0x60
[ 3.493795] __kasan_slab_free+0x43/0x70
[ 3.493913] slab_free_after_rcu_debug+0xad/0x1e0
[ 3.494051] rcu_core+0x5c3/0x9c0
[ 3.494148] handle_softirqs+0x148/0x460
[ 3.494260] __irq_exit_rcu+0x97/0xf0
[ 3.494363] sysvec_apic_timer_interrupt+0x71/0x90
[ 3.494495] asm_sysvec_apic_timer_interrupt+0x1a/0x20
[ 3.494635]
[ 3.494682] Last potentially related work creation:
[ 3.494815] kasan_save_stack+0x33/0x60
[ 3.494923] kasan_record_aux_stack+0x8c/0xa0
[ 3.495044] kmem_cache_free+0x1f5/0x440
[ 3.495153] nf_conntrack_free+0xc1/0x140
[ 3.495264] ctnetlink_del_conntrack+0x4c4/0x520
[ 3.495391] nfnetlink_rcv_msg+0x48e/0x510
[ 3.495505] netlink_rcv_skb+0xc9/0x1f0
[ 3.495611] nfnetlink_rcv+0xdb/0x220
[ 3.495714] netlink_unicast+0x3ec/0x590
[ 3.495823] netlink_sendmsg+0x397/0x690
[ 3.495932] ____sys_sendmsg+0x538/0x550
[ 3.496041] ___sys_sendmsg+0xfc/0x170
[ 3.496145] __sys_sendmsg+0xf4/0x180
[ 3.496248] do_syscall_64+0xc3/0x6e0
[ 3.496350] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 3.496487]
[ 3.496534] The buggy address belongs to the object at ffff88810597eb40
[ 3.496534] which belongs to the cache nf_conntrack of size 248
[ 3.496866] The buggy address is located 176 bytes inside of
[ 3.496866] freed 248-byte region [ffff88810597eb40, ffff88810597ec38)
[ 3.497187]
[ 3.497234] The buggy address belongs to the physical page:
[ 3.497385] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff88810597e140 pfn:0x10597e
[ 3.497632] flags: 0x200000000000200(workingset|node=0|zone=2)
[ 3.497794] page_type: f5(slab)
[ 3.497884] raw: 0200000000000200 ffff88810463fb40 ffff888104640410 ffff888104640410
[ 3.498089] raw: ffff88810597e140 00000008000c0004 00000000f5000000 0000000000000000
[ 3.498293] page dumped because: kasan: bad access detected
[ 3.498442]
[ 3.498489] Memory state around the buggy address:
[ 3.498619] ffff88810597ea80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 3.498812] ffff88810597eb00: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
[ 3.499005] >ffff88810597eb80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 3.499197] ^
[ 3.499379] ffff88810597ec00: fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc fc
[ 3.499573] ffff88810597ec80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 3.499765] ==================================================================
Fixes: e844a928431f ("netfilter: ctnetlink: allow to dump expectation per master conntrack")
Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
---
net/netfilter/nf_conntrack_netlink.c | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index c9d725fc2d71..65aa44a12d01 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3212,7 +3212,7 @@ ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
{
struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
struct nf_conn *ct = cb->data;
- struct nf_conn_help *help = nfct_help(ct);
+ struct nf_conn_help *help;
u_int8_t l3proto = nfmsg->nfgen_family;
unsigned long last_id = cb->args[1];
struct nf_conntrack_expect *exp;
@@ -3220,6 +3220,10 @@ ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
if (cb->args[0])
return 0;
+ help = nfct_help(ct);
+ if (!help)
+ return 0;
+
rcu_read_lock();
restart:
@@ -3249,6 +3253,24 @@ ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
+static int ctnetlink_dump_exp_ct_start(struct netlink_callback *cb)
+{
+ struct nf_conn *ct = cb->data;
+
+ if (!refcount_inc_not_zero(&ct->ct_general.use))
+ return -ENOENT;
+ return 0;
+}
+
+static int ctnetlink_dump_exp_ct_done(struct netlink_callback *cb)
+{
+ struct nf_conn *ct = cb->data;
+
+ if (ct)
+ nf_ct_put(ct);
+ return 0;
+}
+
static int ctnetlink_dump_exp_ct(struct net *net, struct sock *ctnl,
struct sk_buff *skb,
const struct nlmsghdr *nlh,
@@ -3264,6 +3286,8 @@ static int ctnetlink_dump_exp_ct(struct net *net, struct sock *ctnl,
struct nf_conntrack_zone zone;
struct netlink_dump_control c = {
.dump = ctnetlink_exp_ct_dump_table,
+ .start = ctnetlink_dump_exp_ct_start,
+ .done = ctnetlink_dump_exp_ct_done,
};
err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER,
--
2.43.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-03-07 17:21 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-07 17:21 [PATCH net] netfilter: ctnetlink: fix use-after-free in ctnetlink_dump_exp_ct() Hyunwoo Kim
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox