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.
Showing the changes to include/linux (1 of 4 files). Show all
Unified · Split
| @@ -35,22 +35,30 @@ struct nsproxy *dup_namespaces(struct nsproxy *orig); · 34 unmodified lines | ||
| 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 |