Repository: opencontainers/runc
File: libcontainer/nsenter/nsexec.c (C)

Code before:
		path = strstr(namespace, ":");
		if (!path)
			bail("failed to parse %s", namespace);
		*path++ = '\0';

		fd = open(path, O_RDONLY);
		if (fd < 0)
			bail("failed to open %s", path);

		ns->fd = fd;
		strncpy(ns->type, namespace, PATH_MAX - 1);
		strncpy(ns->path, path, PATH_MAX - 1);
		ns->path[PATH_MAX - 1] = '\0';
	} while ((namespace = strtok_r(NULL, ",", &saveptr)) != NULL);

	/*
	 * The ordering in which we join namespaces is important. We should
	 * always join the user namespace *first*. This is all guaranteed
	 * from the container_linux.go side of this, so we're just going to
	 * follow the order given to us.
	 */

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

		if (flag == CLONE_NEWUSER) {
			userns_fd = ns->fd;
			if (!rootless)
				continue;
		}

		write_log(DEBUG, "setns(%#x) into %s namespace (with path %s)", flag, ns->type, ns->path);
		if (setns(ns->fd, flag) < 0)
			bail("failed to setns into %s namespace", ns->type);

		close(ns->fd);
	}

	free(namespaces);
	return userns_fd;
}

static inline int sane_kill(pid_t pid, int signum)
{
	if (pid > 0)
		return kill(pid, signum);
	else
		return 0;
}


Review comment (security):
I try to use `retsnoop` to check and found this in v6.5 kernel

```
//  retsnoop -e '*setxattr' -e 'inode_owner_or_capable' -e 'ns_capable' -E -n 'runc:[2:INIT]' -T


21:47:54.401485 -> 21:47:54.401526 TID/PID 69055/69055 (runc:[2:INIT]/runc:[2:INIT]):

FUNCTION CALLS                                         RESULT    DURATION
----------------------------------------------------   --------  --------
→ __x64_sys_setxattr
    → path_setxattr
        → setxattr
            → do_setxattr
                → vfs_setxattr
                    → security_inode_setxattr
                        → selinux_inode_setxattr
                            ↔ inode_owner_or_capable   [false]    1.100us
                        ← selinux_inode_setxattr       [-EPERM]   2.700us
                    ← security_inode_setxattr          [-EPERM]   8.900us
                ← vfs_setxattr                         [-EPERM]  15.300us
            ← do_setxattr                              [-EPERM]  21.400us
        ← setxattr                                     [-EPERM]  24.000us
    ← path_setxattr                                    [-EPERM]  29.500us
← __x64_sys_setxattr                                   [-EPERM]  31.700us
```

https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/security/selinux/hooks.c#L3151

Is it related to idmap here?

Code after:
		fd = open(path, O_RDONLY);
		if (fd < 0)
			bail("failed to open %s", path);

		ns->fd = fd;
		strncpy(ns->type, namespace, PATH_MAX - 1);
		strncpy(ns->path, path, PATH_MAX - 1);
		ns->path[PATH_MAX - 1] = '\0';
	} while ((namespace = strtok_r(NULL, ",", &saveptr)) != NULL);

	/*
	 * The ordering in which we join namespaces is important. We should
	 * always join the user namespace *first*. This is all guaranteed
	 * from the container_linux.go side of this, so we're just going to
	 * follow the order given to us.
	 */

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

		write_log(DEBUG, "setns(%#x) into %s namespace (with path %s)", flag, ns->type, ns->path);
		if (setns(ns->fd, flag) < 0)
			bail("failed to setns into %s namespace", ns->type);

		/*
		 * If we change user namespaces, make sure we switch to root in the
		 * namespace (this matches the logic for unshare(CLONE_NEWUSER)), lots
		 * of things can break if we aren't the right user. See
		 * <https://github.com/opencontainers/runc/issues/4466> for one example.
		 */
		if (flag == CLONE_NEWUSER) {
			if (setresuid(0, 0, 0) < 0)
				bail("failed to become root in user namespace");
		}

		close(ns->fd);
	}

	free(namespaces);
}

static inline int sane_kill(pid_t pid, int signum)
{
	if (pid > 0)
		return kill(pid, signum);
	else
		return 0;
}

void try_unshare(int flags, const char *msg)