Skip to content

Commit e6fb7fe

Browse files
committed
nsexec: allow timens to work with non-rootless userns
The owner of /proc/self/timens_offsets doesn't change after creating a userns, meaning that we need to request stage-0 to write our timens mappings for us. Before this patch, attempting to use timens with a proper userns resulted in: FATA[0000] nsexec-1[18564]: failed to update /proc/self/timens_offsets: Permission denied FATA[0000] nsexec-0[18562]: failed to sync with stage-1: next state: Success ERRO[0000] runc run failed: unable to start container process: can't get final child's PID from pipe: EOF Fixes: ebc2e7c ("Support time namespace") Signed-off-by: Aleksa Sarai <[email protected]>
1 parent 09822c3 commit e6fb7fe

File tree

2 files changed

+51
-5
lines changed

2 files changed

+51
-5
lines changed

libcontainer/nsenter/nsexec.c

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ enum sync_t {
4848
SYNC_MOUNTSOURCES_ACK = 0x47, /* All mount sources have been sent. */
4949
SYNC_MOUNT_IDMAP_PLS = 0x48, /* Tell parent to mount idmap sources. */
5050
SYNC_MOUNT_IDMAP_ACK = 0x49, /* All idmap mounts have been done. */
51+
SYNC_TIMEOFFSETS_PLS = 0x50, /* Request parent to write timens offsets. */
52+
SYNC_TIMEOFFSETS_ACK = 0x51, /* Timens offsets were written. */
5153
};
5254

5355
#define STAGE_SETUP -1
@@ -755,13 +757,13 @@ void receive_idmapsources(int sockfd)
755757
receive_fd_sources(sockfd, "_LIBCONTAINER_IDMAP_FDS");
756758
}
757759

758-
static void update_timens_offsets(char *map, size_t map_len)
760+
static void update_timens_offsets(pid_t pid, char *map, size_t map_len)
759761
{
760762
if (map == NULL || map_len == 0)
761763
return;
762-
write_log(DEBUG, "update /proc/self/timens_offsets to '%s'", map);
763-
if (write_file(map, map_len, "/proc/self/timens_offsets") < 0)
764-
bail("failed to update /proc/self/timens_offsets");
764+
write_log(DEBUG, "update /proc/%d/timens_offsets to '%s'", pid, map);
765+
if (write_file(map, map_len, "/proc/%d/timens_offsets", pid) < 0)
766+
bail("failed to update /proc/%d/timens_offsets", pid);
765767
}
766768

767769
void nsexec(void)
@@ -1008,6 +1010,15 @@ void nsexec(void)
10081010
bail("failed to sync with child: write(SYNC_MOUNT_IDMAP_ACK)");
10091011
}
10101012

1013+
break;
1014+
case SYNC_TIMEOFFSETS_PLS:
1015+
write_log(DEBUG, "stage-1 requested timens offsets to be configured");
1016+
update_timens_offsets(stage1_pid, config.timensoffset, config.timensoffset_len);
1017+
s = SYNC_TIMEOFFSETS_ACK;
1018+
if (write(syncfd, &s, sizeof(s)) != sizeof(s)) {
1019+
sane_kill(stage1_pid, SIGKILL);
1020+
bail("failed to sync with child: write(SYNC_TIMEOFFSETS_ACK)");
1021+
}
10111022
break;
10121023
case SYNC_CHILD_FINISH:
10131024
write_log(DEBUG, "stage-1 complete");
@@ -1161,7 +1172,19 @@ void nsexec(void)
11611172
* was broken, so we'll just do it the long way anyway.
11621173
*/
11631174
try_unshare(config.cloneflags, "remaining namespaces");
1164-
update_timens_offsets(config.timensoffset, config.timensoffset_len);
1175+
1176+
if (config.timensoffset) {
1177+
write_log(DEBUG, "request stage-0 to write timens offsets");
1178+
1179+
s = SYNC_TIMEOFFSETS_PLS;
1180+
if (write(syncfd, &s, sizeof(s)) != sizeof(s))
1181+
bail("failed to sync with parent: write(SYNC_TIMEOFFSETS_PLS)");
1182+
1183+
if (read(syncfd, &s, sizeof(s)) != sizeof(s))
1184+
bail("failed to sync with parent: read(SYNC_TIMEOFFSETS_ACK)");
1185+
if (s != SYNC_TIMEOFFSETS_ACK)
1186+
bail("failed to sync with parent: SYNC_TIMEOFFSETS_ACK: got %u", s);
1187+
}
11651188

11661189
/* Ask our parent to send the mount sources fds. */
11671190
if (config.mountsources) {

tests/integration/timens.bats

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ load helpers
44

55
function setup() {
66
setup_busybox
7+
8+
mkdir -p rootfs/{proc,sys,tmp}
79
}
810

911
function teardown() {
@@ -53,3 +55,24 @@ function teardown() {
5355
grep -E '^monotonic\s+7881\s+2718281$' <<<"$output"
5456
grep -E '^boottime\s+1337\s+3141519$' <<<"$output"
5557
}
58+
59+
@test "runc run [simple timens + userns]" {
60+
requires root
61+
requires timens
62+
63+
update_config ' .linux.namespaces += [{"type": "user"}]
64+
| .linux.uidMappings += [{"hostID": 100000, "containerID": 0, "size": 65534}]
65+
| .linux.gidMappings += [{"hostID": 200000, "containerID": 0, "size": 65534}] '
66+
67+
update_config '.process.args = ["cat", "/proc/self/timens_offsets"]'
68+
update_config '.linux.namespaces += [{"type": "time"}]
69+
| .linux.timeOffsets = {
70+
"monotonic": { "secs": 7881, "nanosecs": 2718281 },
71+
"boottime": { "secs": 1337, "nanosecs": 3141519 }
72+
}'
73+
74+
runc run test_busybox
75+
[ "$status" -eq 0 ]
76+
grep -E '^monotonic\s+7881\s+2718281$' <<<"$output"
77+
grep -E '^boottime\s+1337\s+3141519$' <<<"$output"
78+
}

0 commit comments

Comments
 (0)