The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH] netfilter: nf_log: clear per-net loggers on unregister
@ 2026-08-22 18:46 Chengfeng Ye
  2026-08-23 15:51 ` Florian Westphal
  0 siblings, 1 reply; 3+ messages in thread
From: Chengfeng Ye @ 2026-08-22 18:46 UTC (permalink / raw)
  To: Pablo Neira Ayuso, Florian Westphal, Phil Sutter, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, Gao feng
  Cc: netfilter-devel, coreteam, netdev, linux-kernel, Chengfeng Ye,
	stable

Logger backends unregister their per-network namespace operations before
unregistering their global logger. This leaves a window where a sysctl or
netlink writer can rebind a per-net logger after the old per-net
selection was cleared, but before the global logger registration is
removed.

The race looks like this:

  CPU 0                                 CPU 1
  ----                                  ----
  unregister_pernet_subsys()
    nf_log_unset(net, logger)
      net->nf.nf_loggers[pf] = NULL

                                        lock nf_log_mutex
                                        find logger in loggers[][]
                                        net->nf.nf_loggers[pf] = logger
                                        unlock nf_log_mutex

  nf_log_unregister(logger)
    lock nf_log_mutex
    loggers[pf][type] = NULL
    unlock nf_log_mutex
    synchronize_rcu()
  module exit returns
  module core frees backend memory

Later, a sysctl read or nf_log_packet() reads net->nf.nf_loggers[pf]
and dereferences the stale logger.

nf_log_unregister() only removes the backend from the global logger
table. It does not clear matching net->nf.nf_loggers[] entries that were
rebound by CPU 1 after per-net teardown. Once module unload completes,
those per-net pointers can still reference static data from the unloaded
logger backend, and later readers can dereference freed module memory.

The kernel reported:

  BUG: unable to handle page fault for address: fffffbfff806d2f4
  #PF: supervisor read access in kernel mode
  #PF: error_code(0x0000) - not-present page
  Oops: Oops: 0000 [#1] SMP KASAN NOPTI
  RIP: 0010:nf_log_proc_dostring+0x2aa/0x4d0
  Call Trace:
   proc_sys_call_handler+0x325/0x540
   vfs_read+0x6e1/0xa20
   ksys_read+0xf7/0x1c0
   do_syscall_64+0xf9/0x520
  Modules linked in: [last unloaded: nf_log_syslog]

Fix this by clearing matching per-net logger selections in every live
network namespace while holding nf_log_mutex after removing the global
registrations. Writers that run before this pass are covered by the
per-net clears, and writes that run later can no longer find the logger
in loggers[][]. The existing synchronize_rcu() then covers readers of
both the global and per-net pointers before module memory is released.

Fixes: 5b023fc8d8e0 ("netfilter: enable per netns support for nf_loggers")
Cc: stable@vger.kernel.org
Signed-off-by: Chengfeng Ye <nicoyip.dev@gmail.com>
---
 net/netfilter/nf_log.c | 26 ++++++++++++++++++--------
 1 file changed, 18 insertions(+), 8 deletions(-)

diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c
index f4d80654dfe6..442c69f544a1 100644
--- a/net/netfilter/nf_log.c
+++ b/net/netfilter/nf_log.c
@@ -42,6 +42,18 @@ static struct nf_logger *__find_logger(int pf, const char *str_logger)
 	return NULL;
 }
 
+static void __nf_log_unset(struct net *net, const struct nf_logger *logger)
+{
+	const struct nf_logger *log;
+	int i;
+
+	for (i = 0; i < NFPROTO_NUMPROTO; i++) {
+		log = nft_log_dereference(net->nf.nf_loggers[i]);
+		if (log == logger)
+			RCU_INIT_POINTER(net->nf.nf_loggers[i], NULL);
+	}
+}
+
 int nf_log_set(struct net *net, u_int8_t pf, const struct nf_logger *logger)
 {
 	const struct nf_logger *log;
@@ -62,15 +74,8 @@ EXPORT_SYMBOL(nf_log_set);
 
 void nf_log_unset(struct net *net, const struct nf_logger *logger)
 {
-	int i;
-	const struct nf_logger *log;
-
 	mutex_lock(&nf_log_mutex);
-	for (i = 0; i < NFPROTO_NUMPROTO; i++) {
-		log = nft_log_dereference(net->nf.nf_loggers[i]);
-		if (log == logger)
-			RCU_INIT_POINTER(net->nf.nf_loggers[i], NULL);
-	}
+	__nf_log_unset(net, logger);
 	mutex_unlock(&nf_log_mutex);
 }
 EXPORT_SYMBOL(nf_log_unset);
@@ -112,6 +117,7 @@ EXPORT_SYMBOL(nf_log_register);
 void nf_log_unregister(struct nf_logger *logger)
 {
 	const struct nf_logger *log;
+	struct net *net;
 	int i;
 
 	mutex_lock(&nf_log_mutex);
@@ -120,6 +126,10 @@ void nf_log_unregister(struct nf_logger *logger)
 		if (log == logger)
 			RCU_INIT_POINTER(loggers[i][logger->type], NULL);
 	}
+	rcu_read_lock();
+	for_each_net_rcu(net)
+		__nf_log_unset(net, logger);
+	rcu_read_unlock();
 	mutex_unlock(&nf_log_mutex);
 	synchronize_rcu();
 }
-- 
2.43.0

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] netfilter: nf_log: clear per-net loggers on unregister
  2026-08-22 18:46 [PATCH] netfilter: nf_log: clear per-net loggers on unregister Chengfeng Ye
@ 2026-08-23 15:51 ` Florian Westphal
  2026-08-23 16:40   ` Chengfeng Ye
  0 siblings, 1 reply; 3+ messages in thread
From: Florian Westphal @ 2026-08-23 15:51 UTC (permalink / raw)
  To: Chengfeng Ye
  Cc: Pablo Neira Ayuso, Phil Sutter, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Gao feng,
	netfilter-devel, coreteam, netdev, linux-kernel, stable

Chengfeng Ye <nicoyip.dev@gmail.com> wrote:
> Logger backends unregister their per-network namespace operations before
> unregistering their global logger. This leaves a window where a sysctl or
> netlink writer can rebind a per-net logger after the old per-net
> selection was cleared, but before the global logger registration is
> removed.
> 
> The race looks like this:
> 
>   CPU 0                                 CPU 1
>   ----                                  ----
>   unregister_pernet_subsys()
>     nf_log_unset(net, logger)
>       net->nf.nf_loggers[pf] = NULL
> 
>                                         lock nf_log_mutex
>                                         find logger in loggers[][]
>                                         net->nf.nf_loggers[pf] = logger
>                                         unlock nf_log_mutex
> Later, a sysctl read or nf_log_packet() reads net->nf.nf_loggers[pf]
> and dereferences the stale logger.
> 
> nf_log_unregister() only removes the backend from the global logger
> table. It does not clear matching net->nf.nf_loggers[] entries that were
> rebound by CPU 1 after per-net teardown. Once module unload completes,
> those per-net pointers can still reference static data from the unloaded
> logger backend, and later readers can dereference freed module memory.

Why can't this be fixed by unregistering in reverse order?

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] netfilter: nf_log: clear per-net loggers on unregister
  2026-08-23 15:51 ` Florian Westphal
@ 2026-08-23 16:40   ` Chengfeng Ye
  0 siblings, 0 replies; 3+ messages in thread
From: Chengfeng Ye @ 2026-08-23 16:40 UTC (permalink / raw)
  To: Florian Westphal
  Cc: Pablo Neira Ayuso, Phil Sutter, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Gao feng,
	netfilter-devel, coreteam, netdev, linux-kernel, stable

On Sun, Aug 23, 2026 at 11:51 PM Florian Westphal <fw@strlen.de> wrote:
>
> Chengfeng Ye <nicoyip.dev@gmail.com> wrote:
> > Logger backends unregister their per-network namespace operations before
> > unregistering their global logger. This leaves a window where a sysctl or
> > netlink writer can rebind a per-net logger after the old per-net
> > selection was cleared, but before the global logger registration is
> > removed.
> >
> > The race looks like this:
> >
> >   CPU 0                                 CPU 1
> >   ----                                  ----
> >   unregister_pernet_subsys()
> >     nf_log_unset(net, logger)
> >       net->nf.nf_loggers[pf] = NULL
> >
> >                                         lock nf_log_mutex
> >                                         find logger in loggers[][]
> >                                         net->nf.nf_loggers[pf] = logger
> >                                         unlock nf_log_mutex
> > Later, a sysctl read or nf_log_packet() reads net->nf.nf_loggers[pf]
> > and dereferences the stale logger.
> >
> > nf_log_unregister() only removes the backend from the global logger
> > table. It does not clear matching net->nf.nf_loggers[] entries that were
> > rebound by CPU 1 after per-net teardown. Once module unload completes,
> > those per-net pointers can still reference static data from the unloaded
> > logger backend, and later readers can dereference freed module memory.
>
> Why can't this be fixed by unregistering in reverse order?

Right, that would be much more clean, I will send the v2 right away.

Best,
Chengfeng

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-08-23 16:40 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-22 18:46 [PATCH] netfilter: nf_log: clear per-net loggers on unregister Chengfeng Ye
2026-08-23 15:51 ` Florian Westphal
2026-08-23 16:40   ` Chengfeng Ye

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox