[PATCH RFC] netfilter: ctnetlink: fix deadlock when loading modules

"syzbot" <[email protected]>
Newsgroups dev.linux.lists.syzbot
Message-ID <[email protected]>
The hung task detector reports a deadlock caused by holding the
nfnl_lock(NFNL_SUBSYS_CTNETLINK) mutex while synchronously waiting for a
user-mode helper (request_module) to finish.

INFO: task blocked for more than 5 seconds.
Call Trace:
 <TASK>
 context_switch
 __schedule+0x17e7/0x5630
 __schedule_loop
 schedule+0x164/0x2b0
 schedule_preempt_disabled+0x13/0x30
 __mutex_lock_common
 __mutex_lock+0x7bf/0x1550
 nfnl_lock
 nfnetlink_rcv_msg+0xa69/0x12b0
 netlink_rcv_skb+0x226/0x4a0
 nfnetlink_rcv+0x2b9/0x28c0
 netlink_unicast_kernel
 netlink_unicast+0x7bb/0x940
 netlink_sendmsg+0x813/0xb40
 sock_sendmsg_nosec+0x13a/0x180
 __sock_sendmsg
 __sys_sendto+0x408/0x5a0
 __do_sys_sendto
 __se_sys_sendto
 __x64_sys_sendto+0xde/0x100
 do_syscall_x64
 do_syscall_64+0x174/0x580
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
 </TASK>

The deadlock occurs when a user-space process sends a netlink message to
create a new conntrack entry and attach a helper to it. nfnetlink_rcv_msg()
acquires the subsystem mutex nfnl_lock(NFNL_SUBSYS_CTNETLINK).
ctnetlink_create_conntrack() then attempts to load the helper module by
calling request_module(). request_module() spawns a user-mode helper
process and synchronously waits for it to finish. If the user-mode helper
process tries to send a netlink message that requires the same
NFNL_SUBSYS_CTNETLINK mutex, it will block indefinitely.

To fix this deadlock, the subsystem mutex nfnl_lock(NFNL_SUBSYS_CTNETLINK)
must be released before calling request_module() and re-acquired
afterwards. If the module is successfully loaded, the function returns
-EAGAIN, causing nfnetlink_rcv_msg() to safely replay the message from the
beginning.

However, dropping the subsystem lock without holding a module reference
introduces a use-after-free vulnerability. A concurrent thread could
acquire the lock, unregister the subsystem, and unload the module while the
lock is dropped. When request_module() finishes, it would return execution
to memory that has already been freed.

To safely drop the subsystem lock, we must pin the module in memory using
try_module_get(THIS_MODULE) before dropping the lock, and release it with
module_put(THIS_MODULE) after re-acquiring the lock.

This patch applies this fix to ctnetlink_create_conntrack() and also fixes
the same vulnerability in ctnetlink_parse_nat_setup() where the lock was
previously dropped without holding a module reference.

Fixes: 226c0c0ef2ab ("netfilter: ctnetlink: helper modules load-on-demand support")
Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=c4b20b80ee6a7a2f5012
Link: https://syzkaller.appspot.com/ai_job?id=0258c070-bcf1-409f-9ddb-fa3807ea115d
To: <[email protected]>
To: "David S. Miller" <[email protected]>
To: "Eric Dumazet" <[email protected]>
To: "Florian Westphal" <[email protected]>
To: "Jakub Kicinski" <[email protected]>
To: <[email protected]>
To: <[email protected]>
To: "Paolo Abeni" <[email protected]>
To: "Pablo Neira Ayuso" <[email protected]>
Cc: "Simon Horman" <[email protected]>
Cc: <[email protected]>
Cc: "Phil Sutter" <[email protected]>

---
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 31cbb1b55..43f8e64a2 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1859,13 +1859,17 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct,
 	if (!nat_hook) {
 #ifdef CONFIG_MODULES
 		rcu_read_unlock();
+		if (!try_module_get(THIS_MODULE))
+			return -EOPNOTSUPP;
 		nfnl_unlock(NFNL_SUBSYS_CTNETLINK);
 		if (request_module("nf-nat") < 0) {
 			nfnl_lock(NFNL_SUBSYS_CTNETLINK);
+			module_put(THIS_MODULE);
 			rcu_read_lock();
 			return -EOPNOTSUPP;
 		}
 		nfnl_lock(NFNL_SUBSYS_CTNETLINK);
+		module_put(THIS_MODULE);
 		rcu_read_lock();
 		nat_hook = rcu_dereference(nf_nat_hook);
 		if (nat_hook)
@@ -1878,13 +1882,17 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct,
 	if (err == -EAGAIN) {
 #ifdef CONFIG_MODULES
 		rcu_read_unlock();
+		if (!try_module_get(THIS_MODULE))
+			return -EOPNOTSUPP;
 		nfnl_unlock(NFNL_SUBSYS_CTNETLINK);
 		if (request_module("nf-nat-%u", nf_ct_l3num(ct)) < 0) {
 			nfnl_lock(NFNL_SUBSYS_CTNETLINK);
+			module_put(THIS_MODULE);
 			rcu_read_lock();
 			return -EOPNOTSUPP;
 		}
 		nfnl_lock(NFNL_SUBSYS_CTNETLINK);
+		module_put(THIS_MODULE);
 		rcu_read_lock();
 #else
 		err = -EOPNOTSUPP;
@@ -2253,10 +2261,19 @@ ctnetlink_create_conntrack(struct net *net,
 		if (helper == NULL) {
 			rcu_read_unlock();
 #ifdef CONFIG_MODULES
+			if (!try_module_get(THIS_MODULE)) {
+				err = -EOPNOTSUPP;
+				goto err1;
+			}
+			nfnl_unlock(NFNL_SUBSYS_CTNETLINK);
 			if (request_module("nfct-helper-%s", helpname) < 0) {
+				nfnl_lock(NFNL_SUBSYS_CTNETLINK);
+				module_put(THIS_MODULE);
 				err = -EOPNOTSUPP;
 				goto err1;
 			}
+			nfnl_lock(NFNL_SUBSYS_CTNETLINK);
+			module_put(THIS_MODULE);
 
 			rcu_read_lock();
 			helper = __nf_conntrack_helper_find(helpname,


base-commit: 075b74841bd0065a3bda3440873c747938e69b68
-- 
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).

See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at [email protected].
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.