Skip to content

Commit 36fbf9b

Browse files
committed
try joining the namespaces twice
We should join as many namespaces as possible first except the user namespace, because there may be some ns paths are not owned by the user namespace we want to join, then we can join remainning namespaces after we join/unshare user ns. Please see #4390. Signed-off-by: lifubang <lifubang@acmcoder.com>
1 parent a54d35b commit 36fbf9b

1 file changed

Lines changed: 52 additions & 21 deletions

File tree

libcontainer/nsenter/nsexec.c

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ struct nlconfig_t {
9595
size_t timensoffset_len;
9696
};
9797

98+
struct namespace_t {
99+
int fd;
100+
char type[PATH_MAX];
101+
char path[PATH_MAX];
102+
};
103+
98104
/*
99105
* List of netlink message types sent to us as part of bootstrapping the init.
100106
* These constants are defined in libcontainer/message_linux.go.
@@ -444,16 +450,11 @@ void nl_free(struct nlconfig_t *config)
444450
free(config->data);
445451
}
446452

447-
void join_namespaces(char *nslist)
453+
struct namespace_t *init_namespaces(char *nslist, int *num)
448454
{
449-
int num = 0, i;
450455
char *saveptr = NULL;
451456
char *namespace = strtok_r(nslist, ",", &saveptr);
452-
struct namespace_t {
453-
int fd;
454-
char type[PATH_MAX];
455-
char path[PATH_MAX];
456-
} *namespaces = NULL;
457+
struct namespace_t *namespaces = NULL;
457458

458459
if (!namespace || !strlen(namespace) || !strlen(nslist))
459460
bail("ns paths are empty");
@@ -469,10 +470,10 @@ void join_namespaces(char *nslist)
469470
struct namespace_t *ns;
470471

471472
/* Resize the namespace array. */
472-
namespaces = realloc(namespaces, ++num * sizeof(struct namespace_t));
473+
namespaces = realloc(namespaces, ++*num * sizeof(struct namespace_t));
473474
if (!namespaces)
474475
bail("failed to reallocate namespace array");
475-
ns = &namespaces[num - 1];
476+
ns = &namespaces[*num - 1];
476477

477478
/* Split 'ns:path'. */
478479
path = strstr(namespace, ":");
@@ -490,20 +491,41 @@ void join_namespaces(char *nslist)
490491
ns->path[PATH_MAX - 1] = '\0';
491492
} while ((namespace = strtok_r(NULL, ",", &saveptr)) != NULL);
492493

493-
/*
494-
* The ordering in which we join namespaces is important. We should
495-
* always join the user namespace *first*. This is all guaranteed
496-
* from the container_linux.go side of this, so we're just going to
497-
* follow the order given to us.
498-
*/
494+
return namespaces;
495+
}
496+
497+
void join_namespaces(struct namespace_t *namespaces, int num, bool ignoreError)
498+
{
499+
int i;
499500

500501
for (i = 0; i < num; i++) {
501502
struct namespace_t *ns = &namespaces[i];
502503
int flag = nsflag(ns->type);
503504

505+
if (ns->fd < 0)
506+
continue;
507+
508+
/*
509+
* The ordering in which we join namespaces is important. We should join
510+
* as many namespaces as possible *first* except the user namespace,
511+
* because there may be some ns paths are not owned by the user namespace
512+
* we want to join, then we can join remainning namespaces after we
513+
* join/unshare user ns. (#4390)
514+
*
515+
* When we join remaining namespaces or for rootless container, we should
516+
* always join the user namespace *first*. This is all guaranteed from the
517+
* container_linux.go side of this, so we're just going to follow the order
518+
* given to us.
519+
*/
520+
if (ignoreError && flag == CLONE_NEWUSER)
521+
continue;
522+
504523
write_log(DEBUG, "setns(%#x) into %s namespace (with path %s)", flag, ns->type, ns->path);
505-
if (setns(ns->fd, flag) < 0)
506-
bail("failed to setns into %s namespace", ns->type);
524+
if (setns(ns->fd, flag) < 0) {
525+
if (!ignoreError)
526+
bail("failed to setns into %s namespace", ns->type);
527+
continue;
528+
}
507529

508530
/*
509531
* If we change user namespaces, make sure we switch to root in the
@@ -517,9 +539,8 @@ void join_namespaces(char *nslist)
517539
}
518540

519541
close(ns->fd);
542+
ns->fd = -1;
520543
}
521-
522-
free(namespaces);
523544
}
524545

525546
static inline int sane_kill(pid_t pid, int signum)
@@ -840,6 +861,8 @@ void nsexec(void)
840861
case STAGE_CHILD:{
841862
pid_t stage2_pid = -1;
842863
enum sync_t s;
864+
int nslen = 0;
865+
struct namespace_t *namespaces = NULL;
843866

844867
/* For debugging. */
845868
current_stage = STAGE_CHILD;
@@ -859,8 +882,11 @@ void nsexec(void)
859882
* [stage 2: STAGE_INIT]) would be meaningless). We could send it
860883
* using cmsg(3) but that's just annoying.
861884
*/
862-
if (config.namespaces)
863-
join_namespaces(config.namespaces);
885+
if (config.namespaces) {
886+
namespaces = init_namespaces(config.namespaces, &nslen);
887+
if (nslen > 0)
888+
join_namespaces(namespaces, nslen, !config.is_rootless_euid);
889+
}
864890

865891
/*
866892
* Deal with user namespaces first. They are quite special, as they
@@ -923,6 +949,11 @@ void nsexec(void)
923949
if (setresuid(0, 0, 0) < 0)
924950
bail("failed to become root in user namespace");
925951
}
952+
/* Join remainning namespaces after we join/unshare user ns. */
953+
if (nslen > 0) {
954+
join_namespaces(namespaces, nslen, false);
955+
free(namespaces);
956+
}
926957

927958
/*
928959
* Unshare all of the namespaces. Now, it should be noted that this

0 commit comments

Comments
 (0)