Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nsproxy: fix free_nsproxy() and simplify create_new_namespaces()

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>

+29 -22
+7 -4
include/linux/ns_common.h
··· 114 114 } 115 115 116 116 #define ns_ref_read(__ns) __ns_ref_read(to_ns_common((__ns))) 117 - #define ns_ref_inc(__ns) __ns_ref_inc(to_ns_common((__ns))) 118 - #define ns_ref_get(__ns) __ns_ref_get(to_ns_common((__ns))) 119 - #define ns_ref_put(__ns) __ns_ref_put(to_ns_common((__ns))) 117 + #define ns_ref_inc(__ns) \ 118 + do { if (__ns) __ns_ref_inc(to_ns_common((__ns))); } while (0) 119 + #define ns_ref_get(__ns) \ 120 + ((__ns) ? __ns_ref_get(to_ns_common((__ns))) : false) 121 + #define ns_ref_put(__ns) \ 122 + ((__ns) ? __ns_ref_put(to_ns_common((__ns))) : false) 120 123 #define ns_ref_put_and_lock(__ns, __ns_lock) \ 121 - __ns_ref_dec_and_lock(to_ns_common((__ns)), __ns_lock) 124 + ((__ns) ? __ns_ref_dec_and_lock(to_ns_common((__ns)), __ns_lock) : false) 122 125 123 126 #define ns_ref_active_read(__ns) \ 124 127 ((__ns) ? __ns_ref_active_read(to_ns_common(__ns)) : 0)
+2 -2
include/linux/nsproxy.h
··· 99 99 void exit_cred_namespaces(struct task_struct *tsk); 100 100 void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new); 101 101 int exec_task_namespaces(void); 102 - void free_nsproxy(struct nsproxy *ns); 102 + void deactivate_nsproxy(struct nsproxy *ns); 103 103 int unshare_nsproxy_namespaces(unsigned long, struct nsproxy **, 104 104 struct cred *, struct fs_struct *); 105 105 int __init nsproxy_cache_init(void); ··· 107 107 static inline void put_nsproxy(struct nsproxy *ns) 108 108 { 109 109 if (refcount_dec_and_test(&ns->count)) 110 - free_nsproxy(ns); 110 + deactivate_nsproxy(ns); 111 111 } 112 112 113 113 static inline void get_nsproxy(struct nsproxy *ns)
+20 -16
kernel/nsproxy.c
··· 60 60 return nsproxy; 61 61 } 62 62 63 + static inline void nsproxy_free(struct nsproxy *ns) 64 + { 65 + put_mnt_ns(ns->mnt_ns); 66 + put_uts_ns(ns->uts_ns); 67 + put_ipc_ns(ns->ipc_ns); 68 + put_pid_ns(ns->pid_ns_for_children); 69 + put_time_ns(ns->time_ns); 70 + put_time_ns(ns->time_ns_for_children); 71 + put_cgroup_ns(ns->cgroup_ns); 72 + put_net(ns->net_ns); 73 + kmem_cache_free(nsproxy_cachep, ns); 74 + } 75 + 76 + void deactivate_nsproxy(struct nsproxy *ns) 77 + { 78 + nsproxy_ns_active_put(ns); 79 + nsproxy_free(ns); 80 + } 81 + 63 82 /* 64 83 * Create new nsproxy and all of its the associated namespaces. 65 84 * Return the newly created nsproxy. Do not attach this to the task, ··· 202 183 nsproxy_ns_active_get(new_ns); 203 184 tsk->nsproxy = new_ns; 204 185 return 0; 205 - } 206 - 207 - void free_nsproxy(struct nsproxy *ns) 208 - { 209 - nsproxy_ns_active_put(ns); 210 - 211 - put_mnt_ns(ns->mnt_ns); 212 - put_uts_ns(ns->uts_ns); 213 - put_ipc_ns(ns->ipc_ns); 214 - put_pid_ns(ns->pid_ns_for_children); 215 - put_time_ns(ns->time_ns); 216 - put_time_ns(ns->time_ns_for_children); 217 - put_cgroup_ns(ns->cgroup_ns); 218 - put_net(ns->net_ns); 219 - kmem_cache_free(nsproxy_cachep, ns); 220 186 } 221 187 222 188 /* ··· 342 338 if (nsset->fs && (flags & CLONE_NEWNS) && (flags & ~CLONE_NEWNS)) 343 339 free_fs_struct(nsset->fs); 344 340 if (nsset->nsproxy) 345 - free_nsproxy(nsset->nsproxy); 341 + nsproxy_free(nsset->nsproxy); 346 342 } 347 343 348 344 static int prepare_nsset(unsigned flags, struct nsset *nsset)