Skip to content

Commit f2a8d52

Browse files
author
Christian Brauner
committed
nsproxy: add struct nsset
Add a simple struct nsset. It holds all necessary pieces to switch to a new set of namespaces without leaving a task in a half-switched state which we will make use of in the next patch. This patch switches the existing setns logic over without causing a change in setns() behavior. This brings setns() closer to how unshare() works(). The prepare_ns() function is responsible to prepare all necessary information. This has two reasons. First it minimizes dependencies between individual namespaces, i.e. all install handler can expect that all fields are properly initialized independent in what order they are called in. Second, this makes the code easier to maintain and easier to follow if it needs to be changed. The prepare_ns() helper will only be switched over to use a flags argument in the next patch. Here it will still use nstype as a simple integer argument which was argued would be clearer. I'm not particularly opinionated about this if it really helps or not. The struct nsset itself already contains the flags field since its name already indicates that it can contain information required by different namespaces. None of this should have functional consequences. Signed-off-by: Christian Brauner <[email protected]> Reviewed-by: Serge Hallyn <[email protected]> Cc: Eric W. Biederman <[email protected]> Cc: Serge Hallyn <[email protected]> Cc: Jann Horn <[email protected]> Cc: Michael Kerrisk <[email protected]> Cc: Aleksa Sarai <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 0e698df commit f2a8d52

File tree

12 files changed

+132
-37
lines changed

12 files changed

+132
-37
lines changed

fs/namespace.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3954,16 +3954,18 @@ static void mntns_put(struct ns_common *ns)
39543954
put_mnt_ns(to_mnt_ns(ns));
39553955
}
39563956

3957-
static int mntns_install(struct nsproxy *nsproxy, struct ns_common *ns)
3957+
static int mntns_install(struct nsset *nsset, struct ns_common *ns)
39583958
{
3959-
struct fs_struct *fs = current->fs;
3959+
struct nsproxy *nsproxy = nsset->nsproxy;
3960+
struct fs_struct *fs = nsset->fs;
39603961
struct mnt_namespace *mnt_ns = to_mnt_ns(ns), *old_mnt_ns;
3962+
struct user_namespace *user_ns = nsset->cred->user_ns;
39613963
struct path root;
39623964
int err;
39633965

39643966
if (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) ||
3965-
!ns_capable(current_user_ns(), CAP_SYS_CHROOT) ||
3966-
!ns_capable(current_user_ns(), CAP_SYS_ADMIN))
3967+
!ns_capable(user_ns, CAP_SYS_CHROOT) ||
3968+
!ns_capable(user_ns, CAP_SYS_ADMIN))
39673969
return -EPERM;
39683970

39693971
if (is_anon_ns(mnt_ns))

include/linux/mnt_namespace.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
struct mnt_namespace;
77
struct fs_struct;
88
struct user_namespace;
9+
struct ns_common;
910

1011
extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *,
1112
struct user_namespace *, struct fs_struct *);

include/linux/nsproxy.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,30 @@ struct nsproxy {
4141
};
4242
extern struct nsproxy init_nsproxy;
4343

44+
/*
45+
* A structure to encompass all bits needed to install
46+
* a partial or complete new set of namespaces.
47+
*
48+
* If a new user namespace is requested cred will
49+
* point to a modifiable set of credentials. If a pointer
50+
* to a modifiable set is needed nsset_cred() must be
51+
* used and tested.
52+
*/
53+
struct nsset {
54+
unsigned flags;
55+
struct nsproxy *nsproxy;
56+
struct fs_struct *fs;
57+
const struct cred *cred;
58+
};
59+
60+
static inline struct cred *nsset_cred(struct nsset *set)
61+
{
62+
if (set->flags & CLONE_NEWUSER)
63+
return (struct cred *)set->cred;
64+
65+
return NULL;
66+
}
67+
4468
/*
4569
* the namespaces access rules are:
4670
*

include/linux/proc_ns.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include <linux/ns_common.h>
99

1010
struct pid_namespace;
11-
struct nsproxy;
11+
struct nsset;
1212
struct path;
1313
struct task_struct;
1414
struct inode;
@@ -19,7 +19,7 @@ struct proc_ns_operations {
1919
int type;
2020
struct ns_common *(*get)(struct task_struct *task);
2121
void (*put)(struct ns_common *ns);
22-
int (*install)(struct nsproxy *nsproxy, struct ns_common *ns);
22+
int (*install)(struct nsset *nsset, struct ns_common *ns);
2323
struct user_namespace *(*owner)(struct ns_common *ns);
2424
struct ns_common *(*get_parent)(struct ns_common *ns);
2525
} __randomize_layout;

ipc/namespace.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,14 @@ static void ipcns_put(struct ns_common *ns)
177177
return put_ipc_ns(to_ipc_ns(ns));
178178
}
179179

180-
static int ipcns_install(struct nsproxy *nsproxy, struct ns_common *new)
180+
static int ipcns_install(struct nsset *nsset, struct ns_common *new)
181181
{
182+
struct nsproxy *nsproxy = nsset->nsproxy;
182183
struct ipc_namespace *ns = to_ipc_ns(new);
183184
if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
184-
!ns_capable(current_user_ns(), CAP_SYS_ADMIN))
185+
!ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN))
185186
return -EPERM;
186187

187-
/* Ditch state from the old ipc namespace */
188-
exit_sem(current);
189188
put_ipc_ns(nsproxy->ipc_ns);
190189
nsproxy->ipc_ns = get_ipc_ns(ns);
191190
return 0;

kernel/cgroup/namespace.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,12 @@ static inline struct cgroup_namespace *to_cg_ns(struct ns_common *ns)
9595
return container_of(ns, struct cgroup_namespace, ns);
9696
}
9797

98-
static int cgroupns_install(struct nsproxy *nsproxy, struct ns_common *ns)
98+
static int cgroupns_install(struct nsset *nsset, struct ns_common *ns)
9999
{
100+
struct nsproxy *nsproxy = nsset->nsproxy;
100101
struct cgroup_namespace *cgroup_ns = to_cg_ns(ns);
101102

102-
if (!ns_capable(current_user_ns(), CAP_SYS_ADMIN) ||
103+
if (!ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN) ||
103104
!ns_capable(cgroup_ns->user_ns, CAP_SYS_ADMIN))
104105
return -EPERM;
105106

kernel/nsproxy.c

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <net/net_namespace.h>
2020
#include <linux/ipc_namespace.h>
2121
#include <linux/time_namespace.h>
22+
#include <linux/fs_struct.h>
2223
#include <linux/proc_ns.h>
2324
#include <linux/file.h>
2425
#include <linux/syscalls.h>
@@ -257,12 +258,79 @@ void exit_task_namespaces(struct task_struct *p)
257258
switch_task_namespaces(p, NULL);
258259
}
259260

261+
static void put_nsset(struct nsset *nsset)
262+
{
263+
unsigned flags = nsset->flags;
264+
265+
if (flags & CLONE_NEWUSER)
266+
put_cred(nsset_cred(nsset));
267+
if (nsset->nsproxy)
268+
free_nsproxy(nsset->nsproxy);
269+
}
270+
271+
static int prepare_nsset(int nstype, struct nsset *nsset)
272+
{
273+
struct task_struct *me = current;
274+
275+
nsset->nsproxy = create_new_namespaces(0, me, current_user_ns(), me->fs);
276+
if (IS_ERR(nsset->nsproxy))
277+
return PTR_ERR(nsset->nsproxy);
278+
279+
if (nstype == CLONE_NEWUSER)
280+
nsset->cred = prepare_creds();
281+
else
282+
nsset->cred = current_cred();
283+
if (!nsset->cred)
284+
goto out;
285+
286+
if (nstype == CLONE_NEWNS)
287+
nsset->fs = me->fs;
288+
289+
nsset->flags = nstype;
290+
return 0;
291+
292+
out:
293+
put_nsset(nsset);
294+
return -ENOMEM;
295+
}
296+
297+
/*
298+
* This is the point of no return. There are just a few namespaces
299+
* that do some actual work here and it's sufficiently minimal that
300+
* a separate ns_common operation seems unnecessary for now.
301+
* Unshare is doing the same thing. If we'll end up needing to do
302+
* more in a given namespace or a helper here is ultimately not
303+
* exported anymore a simple commit handler for each namespace
304+
* should be added to ns_common.
305+
*/
306+
static void commit_nsset(struct nsset *nsset)
307+
{
308+
unsigned flags = nsset->flags;
309+
struct task_struct *me = current;
310+
311+
#ifdef CONFIG_USER_NS
312+
if (flags & CLONE_NEWUSER) {
313+
/* transfer ownership */
314+
commit_creds(nsset_cred(nsset));
315+
nsset->cred = NULL;
316+
}
317+
#endif
318+
319+
#ifdef CONFIG_IPC_NS
320+
if (flags & CLONE_NEWIPC)
321+
exit_sem(me);
322+
#endif
323+
324+
/* transfer ownership */
325+
switch_task_namespaces(me, nsset->nsproxy);
326+
nsset->nsproxy = NULL;
327+
}
328+
260329
SYSCALL_DEFINE2(setns, int, fd, int, nstype)
261330
{
262-
struct task_struct *tsk = current;
263-
struct nsproxy *new_nsproxy;
264331
struct file *file;
265332
struct ns_common *ns;
333+
struct nsset nsset = {};
266334
int err;
267335

268336
file = proc_ns_fget(fd);
@@ -274,20 +342,16 @@ SYSCALL_DEFINE2(setns, int, fd, int, nstype)
274342
if (nstype && (ns->ops->type != nstype))
275343
goto out;
276344

277-
new_nsproxy = create_new_namespaces(0, tsk, current_user_ns(), tsk->fs);
278-
if (IS_ERR(new_nsproxy)) {
279-
err = PTR_ERR(new_nsproxy);
345+
err = prepare_nsset(ns->ops->type, &nsset);
346+
if (err)
280347
goto out;
281-
}
282348

283-
err = ns->ops->install(new_nsproxy, ns);
284-
if (err) {
285-
free_nsproxy(new_nsproxy);
286-
goto out;
349+
err = ns->ops->install(&nsset, ns);
350+
if (!err) {
351+
commit_nsset(&nsset);
352+
perf_event_namespaces(current);
287353
}
288-
switch_task_namespaces(tsk, new_nsproxy);
289-
290-
perf_event_namespaces(tsk);
354+
put_nsset(&nsset);
291355
out:
292356
fput(file);
293357
return err;

kernel/pid_namespace.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,13 +378,14 @@ static void pidns_put(struct ns_common *ns)
378378
put_pid_ns(to_pid_ns(ns));
379379
}
380380

381-
static int pidns_install(struct nsproxy *nsproxy, struct ns_common *ns)
381+
static int pidns_install(struct nsset *nsset, struct ns_common *ns)
382382
{
383+
struct nsproxy *nsproxy = nsset->nsproxy;
383384
struct pid_namespace *active = task_active_pid_ns(current);
384385
struct pid_namespace *ancestor, *new = to_pid_ns(ns);
385386

386387
if (!ns_capable(new->user_ns, CAP_SYS_ADMIN) ||
387-
!ns_capable(current_user_ns(), CAP_SYS_ADMIN))
388+
!ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN))
388389
return -EPERM;
389390

390391
/*

kernel/time/namespace.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,16 +280,17 @@ static void timens_put(struct ns_common *ns)
280280
put_time_ns(to_time_ns(ns));
281281
}
282282

283-
static int timens_install(struct nsproxy *nsproxy, struct ns_common *new)
283+
static int timens_install(struct nsset *nsset, struct ns_common *new)
284284
{
285+
struct nsproxy *nsproxy = nsset->nsproxy;
285286
struct time_namespace *ns = to_time_ns(new);
286287
int err;
287288

288289
if (!current_is_single_threaded())
289290
return -EUSERS;
290291

291292
if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
292-
!ns_capable(current_user_ns(), CAP_SYS_ADMIN))
293+
!ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN))
293294
return -EPERM;
294295

295296
timens_set_vvar_page(current, ns);

kernel/user_namespace.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,7 +1253,7 @@ static void userns_put(struct ns_common *ns)
12531253
put_user_ns(to_user_ns(ns));
12541254
}
12551255

1256-
static int userns_install(struct nsproxy *nsproxy, struct ns_common *ns)
1256+
static int userns_install(struct nsset *nsset, struct ns_common *ns)
12571257
{
12581258
struct user_namespace *user_ns = to_user_ns(ns);
12591259
struct cred *cred;
@@ -1274,14 +1274,14 @@ static int userns_install(struct nsproxy *nsproxy, struct ns_common *ns)
12741274
if (!ns_capable(user_ns, CAP_SYS_ADMIN))
12751275
return -EPERM;
12761276

1277-
cred = prepare_creds();
1277+
cred = nsset_cred(nsset);
12781278
if (!cred)
1279-
return -ENOMEM;
1279+
return -EINVAL;
12801280

12811281
put_user_ns(cred->user_ns);
12821282
set_cred_user_ns(cred, get_user_ns(user_ns));
12831283

1284-
return commit_creds(cred);
1284+
return 0;
12851285
}
12861286

12871287
struct ns_common *ns_get_owner(struct ns_common *ns)

0 commit comments

Comments
 (0)