fs/nsfs.c | 53 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 18 deletions(-)
Initial namespaces are statically allocated and exist for the entire
lifetime of the system. They should not participate in active
reference counting.
The recent introduction of active reference counting in commit
3a18f809184b ("ns: add active reference count") added functions that
unconditionally take/drop active references on all namespaces,
including initial ones.
This causes a WARN_ON_ONCE() to trigger when a namespace file for an
initial namespace is evicted:
WARNING: ./include/linux/ns_common.h:314 at nsfs_evict+0x18e/0x200
The same pattern exists in nsproxy_ns_active_get() and
nsproxy_ns_active_put() which could trigger similar warnings when
operating on initial namespaces.
Fix by checking is_initial_namespace() before taking or dropping
active references in:
- nsfs_evict()
- nsproxy_ns_active_get()
- nsproxy_ns_active_put()
Reported-by: syzbot+0b2e79f91ff6579bfa5b@syzkaller.appspotmail.com
Link: https://syzkaller.appspot.com/bug?extid=0b2e79f91ff6579bfa5b
Fixes: 3a18f809184b ("ns: add active reference count")
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
fs/nsfs.c | 53 +++++++++++++++++++++++++++++++++++------------------
1 file changed, 35 insertions(+), 18 deletions(-)
diff --git a/fs/nsfs.c b/fs/nsfs.c
index ba6c8975c82e..eb14f29dc8d3 100644
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -19,6 +19,7 @@
#include <linux/exportfs.h>
#include <linux/nstree.h>
#include <net/net_namespace.h>
+#include <linux/ns_common.h>
#include "mount.h"
#include "internal.h"
@@ -58,8 +59,8 @@ const struct dentry_operations ns_dentry_operations = {
static void nsfs_evict(struct inode *inode)
{
struct ns_common *ns = inode->i_private;
-
- __ns_ref_active_put(ns);
+ if (!is_initial_namespace(ns))
+ __ns_ref_active_put(ns);
clear_inode(inode);
ns->ops->put(ns);
}
@@ -686,24 +687,40 @@ void __init nsfs_init(void)
void nsproxy_ns_active_get(struct nsproxy *ns)
{
- ns_ref_active_get(ns->mnt_ns);
- ns_ref_active_get(ns->uts_ns);
- ns_ref_active_get(ns->ipc_ns);
- ns_ref_active_get(ns->pid_ns_for_children);
- ns_ref_active_get(ns->cgroup_ns);
- ns_ref_active_get(ns->net_ns);
- ns_ref_active_get(ns->time_ns);
- ns_ref_active_get(ns->time_ns_for_children);
+ if (ns->mnt_ns && !is_initial_namespace(&ns->mnt_ns->ns))
+ ns_ref_active_get(ns->mnt_ns);
+ if (ns->uts_ns && !is_initial_namespace(&ns->uts_ns->ns))
+ ns_ref_active_get(ns->uts_ns);
+ if (ns->ipc_ns && !is_initial_namespace(&ns->ipc_ns->ns))
+ ns_ref_active_get(ns->ipc_ns);
+ if (ns->pid_ns_for_children && !is_initial_namespace(&ns->pid_ns_for_children->ns))
+ ns_ref_active_get(ns->pid_ns_for_children);
+ if (ns->cgroup_ns && !is_initial_namespace(&ns->cgroup_ns->ns))
+ ns_ref_active_get(ns->cgroup_ns);
+ if (ns->net_ns && !is_initial_namespace(&ns->net_ns->ns))
+ ns_ref_active_get(ns->net_ns);
+ if (ns->time_ns && !is_initial_namespace(&ns->time_ns->ns))
+ ns_ref_active_get(ns->time_ns);
+ if (ns->time_ns_for_children && !is_initial_namespace(&ns->time_ns_for_children->ns))
+ ns_ref_active_get(ns->time_ns_for_children);
}
void nsproxy_ns_active_put(struct nsproxy *ns)
{
- ns_ref_active_put(ns->mnt_ns);
- ns_ref_active_put(ns->uts_ns);
- ns_ref_active_put(ns->ipc_ns);
- ns_ref_active_put(ns->pid_ns_for_children);
- ns_ref_active_put(ns->cgroup_ns);
- ns_ref_active_put(ns->net_ns);
- ns_ref_active_put(ns->time_ns);
- ns_ref_active_put(ns->time_ns_for_children);
+ if (ns->mnt_ns && !is_initial_namespace(&ns->mnt_ns->ns))
+ ns_ref_active_put(ns->mnt_ns);
+ if (ns->uts_ns && !is_initial_namespace(&ns->uts_ns->ns))
+ ns_ref_active_put(ns->uts_ns);
+ if (ns->ipc_ns && !is_initial_namespace(&ns->ipc_ns->ns))
+ ns_ref_active_put(ns->ipc_ns);
+ if (ns->pid_ns_for_children && !is_initial_namespace(&ns->pid_ns_for_children->ns))
+ ns_ref_active_put(ns->pid_ns_for_children);
+ if (ns->cgroup_ns && !is_initial_namespace(&ns->cgroup_ns->ns))
+ ns_ref_active_put(ns->cgroup_ns);
+ if (ns->net_ns && !is_initial_namespace(&ns->net_ns->ns))
+ ns_ref_active_put(ns->net_ns);
+ if (ns->time_ns && !is_initial_namespace(&ns->time_ns->ns))
+ ns_ref_active_put(ns->time_ns);
+ if (ns->time_ns_for_children && !is_initial_namespace(&ns->time_ns_for_children->ns))
+ ns_ref_active_put(ns->time_ns_for_children);
}
--
2.43.0
On Sun, Nov 09, 2025 at 02:53:33PM +0530, Deepanshu Kartikey wrote:
> Initial namespaces are statically allocated and exist for the entire
> lifetime of the system. They should not participate in active
> reference counting.
>
> The recent introduction of active reference counting in commit
> 3a18f809184b ("ns: add active reference count") added functions that
> unconditionally take/drop active references on all namespaces,
> including initial ones.
>
> This causes a WARN_ON_ONCE() to trigger when a namespace file for an
> initial namespace is evicted:
>
> WARNING: ./include/linux/ns_common.h:314 at nsfs_evict+0x18e/0x200
>
> The same pattern exists in nsproxy_ns_active_get() and
> nsproxy_ns_active_put() which could trigger similar warnings when
> operating on initial namespaces.
>
> Fix by checking is_initial_namespace() before taking or dropping
> active references in:
> - nsfs_evict()
> - nsproxy_ns_active_get()
> - nsproxy_ns_active_put()
>
> Reported-by: syzbot+0b2e79f91ff6579bfa5b@syzkaller.appspotmail.com
> Link: https://syzkaller.appspot.com/bug?extid=0b2e79f91ff6579bfa5b
> Fixes: 3a18f809184b ("ns: add active reference count")
> Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
> ---
This is not the way to fix it and it's not the cause of the bug.
I've sent a series that addresses this issue properly and it's already
been in next.
© 2016 - 2026 Red Hat, Inc.