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

[PATCH] namespaces: fix exit race by splitting exit

Fix exit race by splitting the nsproxy putting into two pieces. First
piece reduces the nsproxy refcount. If we dropped the last reference, then
it puts the mnt_ns, and returns the nsproxy as a hint to the caller. Else
it returns NULL. The second piece of exiting task namespaces sets
tsk->nsproxy to NULL, and drops the references to other namespaces and
frees the nsproxy only if an nsproxy was passed in.

A little awkward and should probably be reworked, but hopefully it fixes
the NFS oops.

Signed-off-by: Serge E. Hallyn <serue@us.ibm.com>
Cc: Herbert Poetzl <herbert@13thfloor.at>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Cedric Le Goater <clg@fr.ibm.com>
Cc: Daniel Hokka Zakrisson <daniel@hozac.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Serge E. Hallyn and committed by
Linus Torvalds
7a238fcb c0d4d573

+40 -16
+19 -11
include/linux/nsproxy.h
··· 35 35 int copy_namespaces(int flags, struct task_struct *tsk); 36 36 void get_task_namespaces(struct task_struct *tsk); 37 37 void free_nsproxy(struct nsproxy *ns); 38 + struct nsproxy *put_nsproxy(struct nsproxy *ns); 38 39 39 - static inline void put_nsproxy(struct nsproxy *ns) 40 + static inline void finalize_put_nsproxy(struct nsproxy *ns) 40 41 { 41 - if (atomic_dec_and_test(&ns->count)) { 42 + if (ns) 42 43 free_nsproxy(ns); 43 - } 44 44 } 45 45 46 - static inline void exit_task_namespaces(struct task_struct *p) 46 + static inline void put_and_finalize_nsproxy(struct nsproxy *ns) 47 47 { 48 - struct nsproxy *ns = p->nsproxy; 49 - if (ns) { 50 - task_lock(p); 51 - p->nsproxy = NULL; 52 - task_unlock(p); 53 - put_nsproxy(ns); 54 - } 48 + finalize_put_nsproxy(put_nsproxy(ns)); 49 + } 50 + 51 + static inline struct nsproxy *preexit_task_namespaces(struct task_struct *p) 52 + { 53 + return put_nsproxy(p->nsproxy); 54 + } 55 + 56 + static inline void exit_task_namespaces(struct task_struct *p, 57 + struct nsproxy *ns) 58 + { 59 + task_lock(p); 60 + p->nsproxy = NULL; 61 + task_unlock(p); 62 + finalize_put_nsproxy(ns); 55 63 } 56 64 #endif
+4 -2
kernel/exit.c
··· 396 396 current->fs = fs; 397 397 atomic_inc(&fs->count); 398 398 399 - exit_task_namespaces(current); 399 + put_and_finalize_nsproxy(current->nsproxy); 400 400 current->nsproxy = init_task.nsproxy; 401 401 get_task_namespaces(current); 402 402 ··· 853 853 fastcall NORET_TYPE void do_exit(long code) 854 854 { 855 855 struct task_struct *tsk = current; 856 + struct nsproxy *ns; 856 857 int group_dead; 857 858 858 859 profile_task_exit(tsk); ··· 939 938 940 939 tsk->exit_code = code; 941 940 proc_exit_connector(tsk); 941 + ns = preexit_task_namespaces(tsk); 942 942 exit_notify(tsk); 943 - exit_task_namespaces(tsk); 943 + exit_task_namespaces(tsk, ns); 944 944 #ifdef CONFIG_NUMA 945 945 mpol_free(tsk->mempolicy); 946 946 tsk->mempolicy = NULL;
+2 -2
kernel/fork.c
··· 1265 1265 return p; 1266 1266 1267 1267 bad_fork_cleanup_namespaces: 1268 - exit_task_namespaces(p); 1268 + put_and_finalize_nsproxy(p->nsproxy); 1269 1269 bad_fork_cleanup_keys: 1270 1270 exit_keys(p); 1271 1271 bad_fork_cleanup_mm: ··· 1711 1711 } 1712 1712 1713 1713 if (new_nsproxy) 1714 - put_nsproxy(new_nsproxy); 1714 + put_and_finalize_nsproxy(new_nsproxy); 1715 1715 1716 1716 bad_unshare_cleanup_ipc: 1717 1717 if (new_ipc)
+15 -1
kernel/nsproxy.c
··· 117 117 goto out_pid; 118 118 119 119 out: 120 - put_nsproxy(old_ns); 120 + put_and_finalize_nsproxy(old_ns); 121 121 return err; 122 122 123 123 out_pid: ··· 133 133 tsk->nsproxy = old_ns; 134 134 kfree(new_ns); 135 135 goto out; 136 + } 137 + 138 + struct nsproxy *put_nsproxy(struct nsproxy *ns) 139 + { 140 + if (ns) { 141 + if (atomic_dec_and_test(&ns->count)) { 142 + if (ns->mnt_ns) { 143 + put_mnt_ns(ns->mnt_ns); 144 + ns->mnt_ns = NULL; 145 + } 146 + return ns; 147 + } 148 + } 149 + return NULL; 136 150 } 137 151 138 152 void free_nsproxy(struct nsproxy *ns)