Skip to content

Commit

Permalink
Merge branch 'main' into fix/fd-shares
Browse files Browse the repository at this point in the history
  • Loading branch information
yabberyabber authored Dec 2, 2024
2 parents 54cff3d + 1a3c630 commit 366446c
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 41 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ bst
bst-unpersist
bst-init
tags
version/
bst_limits./
90 changes: 49 additions & 41 deletions ns.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,29 +61,31 @@ bool is_nsfd_current(int nsfd, const char *name)
return self.st_ino == stat.st_ino;
}

static int is_setns(const struct nsid *ns)
void opts_to_nsactions(const char *shares[], enum nsaction *nsactions)
{
switch (ns->action) {
case NSACTION_UNSHARE:
case NSACTION_SHARE_WITH_PARENT:
return 0;
default:
return 1;
}
}
for (enum nstype ns = 0; ns < MAX_NS; ns++) {
const char *share = shares[ns];
if (share == NULL) {
nsactions[ns] = NSACTION_UNSHARE;
} else if (share == SHARE_WITH_PARENT) {
nsactions[ns] = NSACTION_SHARE_WITH_PARENT;
} else {
nsactions[ns] = open(share, O_RDONLY | O_CLOEXEC);
if (nsactions[ns] < 0) {
err(1, "open %s", share);
}

/* cmp_nsids compares two ns IDs in a stable manner, such that
namespaces that are entered via setns are sorted before those that
are entered via unshare (or not changed at all). */
static int cmp_nsids(const void *lhs, const void *rhs)
{
int diff = is_setns(rhs) - is_setns(lhs);
if (diff != 0) {
return diff;
/* If the nsfd refers to the same namespace as the one we are
currently in, treat as for SHARE_WITH_PARENT.
We want to give semantics to --share <ns>=/proc/self/ns/<ns>
being the same as --share <ns>. */
if (is_nsfd_current(nsactions[ns], ns_name(ns))) {
close(nsactions[ns]);
nsactions[ns] = NSACTION_SHARE_WITH_PARENT;
}
}
}
/* Both namespaces are the same kind -- keep ordering intact by comparing
pointer values. */
return (int) ((intptr_t) lhs - (intptr_t) rhs);
}

static void ns_enter_one(struct nsid *ns)
Expand Down Expand Up @@ -113,6 +115,8 @@ static void ns_enter_one(struct nsid *ns)
}
}

// Note that for namespaces that want to enter into a specific namespace,
// we actually setns those before forking.
static bool is_postfork_ns(struct nsid *ns)
{
/* For now, only the cgroup namespace needs to be unshared postfork */
Expand All @@ -124,40 +128,44 @@ void ns_enter_prefork(struct nsid *namespaces, size_t *len)
/* Enter all relevant namespaces. It's hard to check in advance which
namespaces are supported, so we unshare them one by one in order. */

/* If we have CAP_SYS_ADMIN from the get-go, starting by entering
the userns may restrict us from joining additional namespaces, so
we rearrange the order so that we setns into target nsfs files first. */
if (capable(BST_CAP_SYS_ADMIN)) {
qsort(namespaces, *len, sizeof (namespaces[0]),
cmp_nsids);
}

struct nsid *first_postfork = NULL;
// First we setns the things that have a specific fd to share into.
struct nsid *ns = &namespaces[0];
for (; ns < namespaces + *len; ++ns) {
if (ns->action != NSACTION_SHARE_WITH_PARENT && is_postfork_ns(ns)) {
first_postfork = ns;
break;
if (ns->action < 0) {
continue;
}
// Note that we also setns the postfork namespaces here. If they
// have a specific namespace to share into then we must share into
// that namespace while we are still in the user namespace of that
// target namespace.
ns_enter_one(ns);
}

size_t i = 0;
for (; ns < namespaces + *len; ++ns, ++i) {
if (first_postfork != NULL && !is_postfork_ns(ns)) {
errx(1, "incompatible options: %s namespace must be entered before "
"forking, but must be done after %s namespace is entered post-fork.",
ns_name(ns->ns),
ns_name(first_postfork->ns));
// Then setns the things that just need a blanket unshare (postfork
// namespaces with NSACTION_UNSHARE need to be shard post-fork).
ns = &namespaces[0];
for (; ns < namespaces + *len; ++ns) {
if (is_postfork_ns(ns)) {
continue;
}
namespaces[i] = *ns;
if (ns->action >= 0) {
continue;
}
ns_enter_one(ns);
}
*len = i;
}

void ns_enter_postfork(struct nsid *namespaces, size_t len)
{
for (struct nsid *ns = &namespaces[0]; ns < namespaces + len; ++ns) {
if (!is_postfork_ns(ns)) {
// Already handled in ns_enter_prefork.
continue;
}
if (ns->action >= 0) {
// If there is an fd action then we already did this prefork.
continue;
}
ns_enter_one(ns);
}
}

0 comments on commit 366446c

Please sign in to comment.