Skip to content

Commit cefd55b

Browse files
committed
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: 3c9820d ("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>
1 parent 18b5c40 commit cefd55b

3 files changed

Lines changed: 29 additions & 22 deletions

File tree

include/linux/ns_common.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,14 @@ static __always_inline __must_check bool __ns_ref_dec_and_lock(struct ns_common
114114
}
115115

116116
#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)
120123
#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)
122125

123126
#define ns_ref_active_read(__ns) \
124127
((__ns) ? __ns_ref_active_read(to_ns_common(__ns)) : 0)

include/linux/nsproxy.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,15 @@ void get_cred_namespaces(struct task_struct *tsk);
9999
void exit_cred_namespaces(struct task_struct *tsk);
100100
void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new);
101101
int exec_task_namespaces(void);
102-
void free_nsproxy(struct nsproxy *ns);
102+
void deactivate_nsproxy(struct nsproxy *ns);
103103
int unshare_nsproxy_namespaces(unsigned long, struct nsproxy **,
104104
struct cred *, struct fs_struct *);
105105
int __init nsproxy_cache_init(void);
106106

107107
static inline void put_nsproxy(struct nsproxy *ns)
108108
{
109109
if (refcount_dec_and_test(&ns->count))
110-
free_nsproxy(ns);
110+
deactivate_nsproxy(ns);
111111
}
112112

113113
static inline void get_nsproxy(struct nsproxy *ns)

kernel/nsproxy.c

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,25 @@ static inline struct nsproxy *create_nsproxy(void)
6060
return nsproxy;
6161
}
6262

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+
6382
/*
6483
* Create new nsproxy and all of its the associated namespaces.
6584
* 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)
185204
return 0;
186205
}
187206

188-
void free_nsproxy(struct nsproxy *ns)
189-
{
190-
nsproxy_ns_active_put(ns);
191-
192-
put_mnt_ns(ns->mnt_ns);
193-
put_uts_ns(ns->uts_ns);
194-
put_ipc_ns(ns->ipc_ns);
195-
put_pid_ns(ns->pid_ns_for_children);
196-
put_time_ns(ns->time_ns);
197-
put_time_ns(ns->time_ns_for_children);
198-
put_cgroup_ns(ns->cgroup_ns);
199-
put_net(ns->net_ns);
200-
kmem_cache_free(nsproxy_cachep, ns);
201-
}
202-
203207
/*
204208
* Called from unshare. Unshare all the namespaces part of nsproxy.
205209
* On success, returns the new nsproxy.
@@ -338,7 +342,7 @@ static void put_nsset(struct nsset *nsset)
338342
if (nsset->fs && (flags & CLONE_NEWNS) && (flags & ~CLONE_NEWNS))
339343
free_fs_struct(nsset->fs);
340344
if (nsset->nsproxy)
341-
free_nsproxy(nsset->nsproxy);
345+
nsproxy_free(nsset->nsproxy);
342346
}
343347

344348
static int prepare_nsset(unsigned flags, struct nsset *nsset)

0 commit comments

Comments
 (0)