[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].