[PATCH net] netfilter: xt_recent: do not call remove_proc_entry() under recent_lock
flat view
WARM1d
From: Cen Zhang (Microsoft) <hidden>
Date: 2026-10-28 21:45:01
Also in:
lkml, netfilter-devel, stable
Subsystem:
netfilter, networking [general], the rest · Maintainers:
Pablo Neira Ayuso, Florian Westphal, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
recent_proc_net_exit() removes the /proc/net/xt_recent/<name> entries of
a dying network namespace while holding spin_lock_bh(&recent_lock).
remove_proc_entry() may sleep: proc_entry_rundown() waits until every
task that is still inside the entry's file operations has left. A reader
of such an entry is counted in by proc_reg_read() before
recent_seq_start() takes recent_lock. So if a read is in flight when the
namespace is torn down, cleanup_net() sleeps in atomic context holding
the spinlock that the reader is spinning on, and neither side can ever
make progress.
An unprivileged user can trigger this from a user namespace by adding a
rule with the "recent" match and reading its proc file while the
namespace is being destroyed. As recent_lock is global, every later
attempt to take it spins forever too: the spinning can spread to all
CPUs, and network namespace teardown stops system-wide.
BUG: sleeping function called from invalid context at
kernel/sched/completion.c:116
#3: (recent_lock){+...}-{3:3}, at: recent_net_exit+0x2b/0x140
Workqueue: netns cleanup_net
Call Trace:
proc_entry_rundown+0x185/0x1b0
remove_proc_entry+0x21a/0x520
recent_net_exit+0x7f/0x140
cleanup_net+0x414/0x8f0
...
rcu: INFO: rcu_preempt self-detected stall on CPU
Call Trace:
queued_spin_lock_slowpath+0x313/0xc20
recent_seq_start+0x87/0x1b0
seq_read_iter+0x265/0x11e0
proc_reg_read+0x1ab/0x270
Fix by taking recent_mutex instead of recent_lock. According to
commit a0e889bb1bdc ("[NETFILTER]: recent match: fix "sleeping function
called from invalid context""), recent_mutex is the lock to use here: it
protects recent_net->tables and was introduced so that proc entries are
created and removed without recent_lock held, because those calls may
sleep. hashlimit_proc_net_exit() does the same for xt_hashlimit.
Fixes: 665e205c16c1 ("netfilter: xt_recent: fix namespace destroy path")
Cc: <redacted>
Cc: <redacted>
Signed-off-by: Cen Zhang (Microsoft) <redacted>
---
net/netfilter/xt_recent.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c
index 55f1f2d89952..00cccf003cff 100644
--- a/net/netfilter/xt_recent.c
+++ b/net/netfilter/xt_recent.c@@ -650,14 +650,15 @@ static void __net_exit recent_proc_net_exit(struct net *net) /* recent_net_exit() is called before recent_mt_destroy(). Make sure * that the parent xt_recent proc entry is empty before trying to - * remove it. + * remove it. Use recent_mutex instead of recent_lock since + * remove_proc_entry() may sleep. */ - spin_lock_bh(&recent_lock); + mutex_lock(&recent_mutex); list_for_each_entry(t, &recent_net->tables, list) remove_proc_entry(t->name, recent_net->xt_recent); recent_net->xt_recent = NULL; - spin_unlock_bh(&recent_lock); + mutex_unlock(&recent_mutex); remove_proc_entry("xt_recent", net->proc_net); }
--
2.55.0