]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
nsproxy: fix free_nsproxy() and simplify create_new_namespaces()
authorChristian Brauner <brauner@kernel.org>
Tue, 11 Nov 2025 21:29:44 +0000 (22:29 +0100)
committerChristian Brauner <brauner@kernel.org>
Fri, 14 Nov 2025 12:10:38 +0000 (13:10 +0100)
Make it possible to handle NULL being passed to the reference count
helpers instead of forcing the caller to handle this. Afterwards we can
nicely allow a cleanup guard to handle nsproxy freeing.

Active reference count handling is not done in nsproxy_free() but rather
in free_nsproxy() as nsproxy_free() is also called from setns() failure
paths where a new nsproxy has been prepared but has not been marked as
active via switch_task_namespaces().

Link: https://lore.kernel.org/690bfb9e.050a0220.2e3c35.0013.GAE@google.com
Link: https://patch.msgid.link/20251111-sakralbau-guthaben-7dcc277d337f@brauner
Fixes: 3c9820d5c64a ("ns: add active reference count")
Reported-by: syzbot+0b2e79f91ff6579bfa5b@syzkaller.appspotmail.com
Reported-by: syzbot+0a8655a80e189278487e@syzkaller.appspotmail.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
include/linux/ns_common.h
include/linux/nsproxy.h
kernel/nsproxy.c

index 136f6a322e53692be6b0b9acc461681ec1f0e3ad..825f5865bfc5aeef9f3343380e64d8c18749d2c7 100644 (file)
@@ -114,11 +114,14 @@ static __always_inline __must_check bool __ns_ref_dec_and_lock(struct ns_common
 }
 
 #define ns_ref_read(__ns) __ns_ref_read(to_ns_common((__ns)))
-#define ns_ref_inc(__ns) __ns_ref_inc(to_ns_common((__ns)))
-#define ns_ref_get(__ns) __ns_ref_get(to_ns_common((__ns)))
-#define ns_ref_put(__ns) __ns_ref_put(to_ns_common((__ns)))
+#define ns_ref_inc(__ns) \
+       do { if (__ns) __ns_ref_inc(to_ns_common((__ns))); } while (0)
+#define ns_ref_get(__ns) \
+       ((__ns) ? __ns_ref_get(to_ns_common((__ns))) : false)
+#define ns_ref_put(__ns) \
+       ((__ns) ? __ns_ref_put(to_ns_common((__ns))) : false)
 #define ns_ref_put_and_lock(__ns, __ns_lock) \
-       __ns_ref_dec_and_lock(to_ns_common((__ns)), __ns_lock)
+       ((__ns) ? __ns_ref_dec_and_lock(to_ns_common((__ns)), __ns_lock) : false)
 
 #define ns_ref_active_read(__ns) \
        ((__ns) ? __ns_ref_active_read(to_ns_common(__ns)) : 0)
index ac825eddec5969f395ea5b303f182527043e6fa5..5a67648721c7d9c5ce639ea6a007cddda984819b 100644 (file)
@@ -99,7 +99,7 @@ void get_cred_namespaces(struct task_struct *tsk);
 void exit_cred_namespaces(struct task_struct *tsk);
 void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new);
 int exec_task_namespaces(void);
-void free_nsproxy(struct nsproxy *ns);
+void deactivate_nsproxy(struct nsproxy *ns);
 int unshare_nsproxy_namespaces(unsigned long, struct nsproxy **,
        struct cred *, struct fs_struct *);
 int __init nsproxy_cache_init(void);
@@ -107,7 +107,7 @@ int __init nsproxy_cache_init(void);
 static inline void put_nsproxy(struct nsproxy *ns)
 {
        if (refcount_dec_and_test(&ns->count))
-               free_nsproxy(ns);
+               deactivate_nsproxy(ns);
 }
 
 static inline void get_nsproxy(struct nsproxy *ns)
index 94c2cfe0afa14538d2ed3faa885865b260bc2472..259c4b4f1eeb96288669854b816235dd01d9e5e0 100644 (file)
@@ -60,6 +60,25 @@ static inline struct nsproxy *create_nsproxy(void)
        return nsproxy;
 }
 
+static inline void nsproxy_free(struct nsproxy *ns)
+{
+       put_mnt_ns(ns->mnt_ns);
+       put_uts_ns(ns->uts_ns);
+       put_ipc_ns(ns->ipc_ns);
+       put_pid_ns(ns->pid_ns_for_children);
+       put_time_ns(ns->time_ns);
+       put_time_ns(ns->time_ns_for_children);
+       put_cgroup_ns(ns->cgroup_ns);
+       put_net(ns->net_ns);
+       kmem_cache_free(nsproxy_cachep, ns);
+}
+
+void deactivate_nsproxy(struct nsproxy *ns)
+{
+       nsproxy_ns_active_put(ns);
+       nsproxy_free(ns);
+}
+
 /*
  * Create new nsproxy and all of its the associated namespaces.
  * Return the newly created nsproxy.  Do not attach this to the task,
@@ -185,21 +204,6 @@ int copy_namespaces(u64 flags, struct task_struct *tsk)
        return 0;
 }
 
-void free_nsproxy(struct nsproxy *ns)
-{
-       nsproxy_ns_active_put(ns);
-
-       put_mnt_ns(ns->mnt_ns);
-       put_uts_ns(ns->uts_ns);
-       put_ipc_ns(ns->ipc_ns);
-       put_pid_ns(ns->pid_ns_for_children);
-       put_time_ns(ns->time_ns);
-       put_time_ns(ns->time_ns_for_children);
-       put_cgroup_ns(ns->cgroup_ns);
-       put_net(ns->net_ns);
-       kmem_cache_free(nsproxy_cachep, ns);
-}
-
 /*
  * Called from unshare. Unshare all the namespaces part of nsproxy.
  * On success, returns the new nsproxy.
@@ -338,7 +342,7 @@ static void put_nsset(struct nsset *nsset)
        if (nsset->fs && (flags & CLONE_NEWNS) && (flags & ~CLONE_NEWNS))
                free_fs_struct(nsset->fs);
        if (nsset->nsproxy)
-               free_nsproxy(nsset->nsproxy);
+               nsproxy_free(nsset->nsproxy);
 }
 
 static int prepare_nsset(unsigned flags, struct nsset *nsset)