16#include <linux/rtnetlink.h>
24#include <sys/syscall.h>
28#define CHECK(expression) \
30 if (!(expression)) { \
31 fprintf(stderr, "NAMESPACE-CONTRACT: FAIL line=%d %s errno=%d\n", __LINE__, #expression, \
37static pid_t clone_process(
unsigned long flags) {
38 return syscall(SYS_clone, flags | SIGCHLD, NULL, NULL, NULL, 0);
41static int reap(pid_t child) {
43 CHECK(child > 0 && waitpid(child, &status, 0) == child);
44 CHECK(WIFEXITED(status) && WEXITSTATUS(status) == 0);
48static int pid_child(
int report,
int ready_read,
int ready_write, pid_t outside) {
49 CHECK(getpid() == 1 && syscall(SYS_gettid) == 1 && getppid() == 0);
51 CHECK(kill(outside, 0) == -1 && errno == ESRCH);
55 CHECK(getpid() == 2 && syscall(SYS_gettid) == 2 && getppid() == 1);
60 pid_t outer = strtol(
identity, NULL, 10);
61 CHECK(outer > 2 && write(report, &outer,
sizeof(outer)) ==
sizeof(outer));
62 CHECK(write(ready_write,
"x", 1) == 1);
69 CHECK(read(ready_read, &ready, 1) == 1);
74static int pending_pid_namespace(
void) {
75 pid_t before = getpid();
76 CHECK(unshare(CLONE_NEWPID) == 0 && getpid() == before);
80 _exit(getpid() == 1 && syscall(SYS_gettid) == 1 && getppid() == 0 ? 0 : 1);
84 CHECK(fork() == -1 && errno == ENOMEM);
88static int pid_namespace(
void) {
89 const pid_t before = getpid();
90 int report[2], ready[2];
91 CHECK(pipe(report) == 0 && pipe(ready) == 0);
92 pid_t child = clone_process(CLONE_NEWPID);
95 _exit(pid_child(report[1], ready[0], ready[1], before));
100 pid_t descendant = 0;
101 CHECK(read(report[0], &descendant,
sizeof(descendant)) ==
sizeof(descendant));
104 CHECK(getpid() == before && syscall(SYS_gettid) == before);
106 CHECK(kill(descendant, 0) == -1 && errno == ESRCH);
110 _exit(pending_pid_namespace());
113 puts(
"NAMESPACE-CONTRACT: pid PASS");
122static int ipc_child(key_t key,
int parent_sem,
int parent_shm,
int parent_msg,
123 int* inherited_mapping,
const char* name) {
125 CHECK(semget(key, 1, 0) == -1 && errno == ENOENT);
127 CHECK(semctl(parent_sem, 0, GETVAL) == -1 && errno == EINVAL);
129 CHECK(shmget(key, 4096, 0) == -1 && errno == ENOENT);
131 CHECK(shmat(parent_shm, NULL, 0) == (
void*)-1 && errno == EINVAL);
133 CHECK(msgget(key, 0) == -1 && errno == ENOENT);
134 struct msqid_ds status;
136 CHECK(msgctl(parent_msg, IPC_STAT, &status) == -1 && errno == EINVAL);
138 CHECK(mq_open(name, O_RDONLY) == (mqd_t)-1 && errno == ENOENT);
140 int sem = semget(key, 1, IPC_CREAT | IPC_EXCL | 0600);
141 int shm = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0600);
142 int msg = msgget(key, IPC_CREAT | IPC_EXCL | 0600);
143 CHECK(sem >= 0 && shm >= 0 && msg >= 0);
144 CHECK(semctl(sem, 0, SETVAL, 7) == 0 && semctl(sem, 0, GETVAL) == 7);
145 int* mapping = shmat(shm, NULL, 0);
146 CHECK(mapping != (
void*)-1);
148 CHECK(*inherited_mapping == 17 && shmdt(mapping) == 0);
149 struct mq_attr attr = {.mq_maxmsg = 2, .mq_msgsize = 32};
150 mqd_t mq = mq_open(name, O_CREAT | O_EXCL | O_RDWR, 0600, &attr);
151 CHECK(mq != (mqd_t)-1);
152 CHECK(mq_send(mq,
"private", 8, 0) == 0);
154 CHECK(mq_receive(mq, received,
sizeof(received), NULL) == 8);
155 CHECK(!strcmp(received,
"private") && mq_close(mq) == 0);
160static int ipc_namespace(
void) {
161 key_t key = 0x41000000 | getpid();
163 snprintf(name,
sizeof(name),
"/namespace-%ld", (
long)getpid());
164 int sem = semget(key, 1, IPC_CREAT | IPC_EXCL | 0600);
165 int shm = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0600);
166 int msg = msgget(key, IPC_CREAT | IPC_EXCL | 0600);
167 CHECK(sem >= 0 && shm >= 0 && msg >= 0);
168 CHECK(semctl(sem, 0, SETVAL, 17) == 0);
169 int* mapping = shmat(shm, NULL, 0);
170 CHECK(mapping != (
void*)-1);
172 struct mq_attr attr = {.mq_maxmsg = 2, .mq_msgsize = 32};
173 mqd_t mq = mq_open(name, O_CREAT | O_EXCL | O_RDWR, 0600, &attr);
174 CHECK(mq != (mqd_t)-1 && mq_send(mq,
"parent", 7, 0) == 0);
175 pid_t child = clone_process(CLONE_NEWIPC);
178 _exit(ipc_child(key, sem, shm, msg, mapping, name));
181 CHECK(semctl(sem, 0, GETVAL) == 17 && *mapping == 17);
183 CHECK(mq_receive(mq, received,
sizeof(received), NULL) == 7);
184 CHECK(!strcmp(received,
"parent"));
185 CHECK(mq_close(mq) == 0 && mq_unlink(name) == 0);
186 CHECK(semctl(sem, 0, IPC_RMID) == 0 && msgctl(msg, IPC_RMID, NULL) == 0);
187 CHECK(shmdt(mapping) == 0 && shmctl(shm, IPC_RMID, NULL) == 0);
188 puts(
"NAMESPACE-CONTRACT: ipc PASS");
192static int write_file(
const char* path,
const char* value) {
193 int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
195 CHECK(write(fd, value, strlen(value)) == (ssize_t)strlen(value));
196 CHECK(close(fd) == 0);
200static int write_control(
const char* path,
const char* value) {
201 int fd = open(path, O_WRONLY);
203 CHECK(write(fd, value, strlen(value)) == (ssize_t)strlen(value));
204 CHECK(close(fd) == 0);
208static int map_user(uid_t uid, gid_t gid) {
210 snprintf(map,
sizeof(map),
"0 %u 1\n", uid);
211 CHECK(!write_control(
"/proc/self/uid_map", map));
212 CHECK(!write_control(
"/proc/self/setgroups",
"deny\n"));
213 snprintf(map,
sizeof(map),
"0 %u 1\n", gid);
214 CHECK(!write_control(
"/proc/self/gid_map", map));
215 CHECK(getuid() == 0 && getgid() == 0);
219static int mount_child(
const char* source,
const char* target,
const char* writable, uid_t uid,
220 gid_t gid,
int inherited_proc) {
221 int self = openat(inherited_proc,
"self", O_PATH | O_DIRECTORY);
223 int uid_map = openat(self,
"uid_map", O_RDONLY);
225 CHECK(close(uid_map) == 0 && close(self) == 0 && close(inherited_proc) == 0);
226 CHECK(!map_user(uid, gid));
227 CHECK(mount(source, target, NULL, MS_BIND, NULL) == 0);
228 char file[512], workspace[512], output[1024];
229 snprintf(file,
sizeof(file),
"%s/sentinel", target);
230 snprintf(workspace,
sizeof(workspace),
"%s/work", target);
231 snprintf(output,
sizeof(output),
"%s/output", workspace);
232 int held = open(file, O_WRONLY);
235 CHECK(mount(NULL, target, NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL) == -1 && errno == EBUSY);
236 CHECK(close(held) == 0);
237 CHECK(mount(NULL, target, NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL) == 0);
238 CHECK(mount(writable, workspace, NULL, MS_BIND, NULL) == 0);
240 CHECK(open(file, O_WRONLY | O_TRUNC) == -1 && errno == EROFS);
241 CHECK(!write_file(output,
"workspace"));
242 pid_t child = clone_process(CLONE_NEWUSER | CLONE_NEWNS);
246 int result = mount(NULL, target, NULL, MS_BIND | MS_REMOUNT, NULL);
247 _exit(result == -1 && errno == EPERM ? 0 : 1);
253static int mount_namespace(
void) {
254 char base[256], source[320], target[320], writable[320], work[384], file[384], output[384];
255 snprintf(base,
sizeof(base),
"/tmp/namespace-contract-%ld", (
long)getpid());
256 snprintf(source,
sizeof(source),
"%s/source", base);
257 snprintf(target,
sizeof(target),
"%s/target", base);
258 snprintf(writable,
sizeof(writable),
"%s/writable", base);
259 snprintf(work,
sizeof(work),
"%s/work", source);
260 snprintf(file,
sizeof(file),
"%s/sentinel", source);
261 snprintf(output,
sizeof(output),
"%s/output", writable);
262 CHECK(mkdir(base, 0700) == 0 && mkdir(source, 0700) == 0 && mkdir(target, 0700) == 0);
263 CHECK(mkdir(writable, 0700) == 0 && mkdir(work, 0700) == 0);
264 CHECK(!write_file(file,
"parent"));
265 uid_t uid = getuid();
266 gid_t gid = getgid();
267 int inherited_proc = open(
"/proc", O_PATH | O_DIRECTORY);
268 CHECK(inherited_proc >= 0);
269 pid_t child = clone_process(CLONE_NEWUSER | CLONE_NEWNS);
272 _exit(mount_child(source, target, writable, uid, gid, inherited_proc));
274 CHECK(close(inherited_proc) == 0);
276 CHECK(!write_file(file,
"parent-still-writable"));
278 snprintf(hidden,
sizeof(hidden),
"%s/sentinel", target);
280 CHECK(access(hidden, F_OK) == -1 && errno == ENOENT);
281 CHECK(access(output, F_OK) == 0);
282 CHECK(unlink(output) == 0 && unlink(file) == 0 && rmdir(work) == 0);
283 CHECK(rmdir(writable) == 0 && rmdir(target) == 0 && rmdir(source) == 0 && rmdir(base) == 0);
284 puts(
"NAMESPACE-CONTRACT: mounts PASS");
288static int route_request(
int route,
void* request,
size_t length) {
289 struct sockaddr_nl kernel = {.nl_family = AF_NETLINK};
290 CHECK(sendto(route, request, length, 0, (
struct sockaddr*)&kernel,
sizeof(kernel)) ==
293 ssize_t received = recv(route, response,
sizeof(response), 0);
294 CHECK(received >= (ssize_t)NLMSG_LENGTH(
sizeof(
struct nlmsgerr)));
295 struct nlmsghdr* header = (
struct nlmsghdr*)response;
296 CHECK(header->nlmsg_type == NLMSG_ERROR && header->nlmsg_len <= (
unsigned)received);
297 struct nlmsgerr* error = NLMSG_DATA(header);
298 CHECK(error->error == 0);
302static int configure_loopback(
void) {
303 int index = if_nametoindex(
"lo");
305 int route = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
307 struct sockaddr_nl local = {.nl_family = AF_NETLINK, .nl_pid = getpid()};
308 CHECK(bind(route, (
struct sockaddr*)&local,
sizeof(local)) == 0);
309 char buffer[128] = {0};
310 struct nlmsghdr* header = (
struct nlmsghdr*)buffer;
311 header->nlmsg_len = NLMSG_LENGTH(
sizeof(
struct ifaddrmsg));
312 header->nlmsg_type = RTM_NEWADDR;
313 header->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
314 header->nlmsg_seq = 1;
315 struct ifaddrmsg* address = NLMSG_DATA(header);
316 address->ifa_family = AF_INET;
317 address->ifa_prefixlen = 8;
318 address->ifa_scope = RT_SCOPE_HOST;
319 address->ifa_index = index;
320 for (
int field = IFA_ADDRESS; field <= IFA_LOCAL; ++field) {
321 struct rtattr* attribute = (
struct rtattr*)(buffer + NLMSG_ALIGN(header->nlmsg_len));
322 attribute->rta_type = field;
323 attribute->rta_len = RTA_LENGTH(
sizeof(uint32_t));
325 memcpy(RTA_DATA(attribute), &loopback,
sizeof(loopback));
326 header->nlmsg_len = NLMSG_ALIGN(header->nlmsg_len) + RTA_ALIGN(attribute->rta_len);
328 CHECK(!route_request(route, buffer, header->nlmsg_len));
329 memset(buffer, 0,
sizeof(buffer));
330 header->nlmsg_len = NLMSG_LENGTH(
sizeof(
struct ifinfomsg));
331 header->nlmsg_type = RTM_NEWLINK;
332 header->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
333 header->nlmsg_seq = 2;
334 struct ifinfomsg* link = NLMSG_DATA(header);
335 link->ifi_index = index;
336 link->ifi_flags = link->ifi_change = IFF_UP;
337 CHECK(!route_request(route, buffer, header->nlmsg_len));
338 CHECK(close(route) == 0);
342static int inet_socket(
int type,
unsigned port) {
343 int fd = socket(AF_INET, type | SOCK_CLOEXEC, 0);
347 struct sockaddr_in address = {
348 .sin_family = AF_INET, .sin_port = htons(port), .sin_addr.s_addr = htonl(
INADDR_LOOPBACK)};
349 if (bind(fd, (
struct sockaddr*)&address,
sizeof(address)) < 0 ||
350 (type == SOCK_STREAM && listen(fd, 4) < 0)) {
357static int network_child(
unsigned port,
const struct sockaddr_un* abstract, socklen_t extent) {
358 CHECK(!configure_loopback());
359 struct sockaddr_in address = {
360 .sin_family = AF_INET, .sin_port = htons(port), .sin_addr.s_addr = htonl(
INADDR_LOOPBACK)};
361 int isolated = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
362 CHECK(isolated >= 0);
364 CHECK(connect(isolated, (
struct sockaddr*)&address,
sizeof(address)) == -1 &&
365 (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EHOSTUNREACH));
366 CHECK(close(isolated) == 0);
367 isolated = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
368 CHECK(isolated >= 0);
370 CHECK(connect(isolated, (
const struct sockaddr*)abstract, extent) == -1 &&
371 (errno == ECONNREFUSED || errno == ENOENT));
372 CHECK(bind(isolated, (
const struct sockaddr*)abstract, extent) == 0);
373 CHECK(close(isolated) == 0);
375 int listener = inet_socket(SOCK_STREAM, port);
376 int udp = inet_socket(SOCK_DGRAM, port);
377 CHECK(listener >= 0 && udp >= 0);
378 pid_t client = fork();
381 int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
382 if (socket_fd < 0 || connect(socket_fd, (
struct sockaddr*)&address,
sizeof(address)) ||
383 write(socket_fd,
"private", 8) != 8) {
387 _exit(read(socket_fd, &reply, 1) == 1 && reply ==
'x' ? 0 : 1);
389 int accepted = accept(listener, NULL, NULL);
390 CHECK(accepted >= 0);
392 CHECK(read(accepted, received,
sizeof(received)) == 8 && !strcmp(received,
"private"));
393 CHECK(write(accepted,
"x", 1) == 1 && close(accepted) == 0);
394 CHECK(!reap(client));
395 int sender = socket(AF_INET, SOCK_DGRAM, 0);
397 CHECK(sendto(sender,
"datagram", 9, 0, (
struct sockaddr*)&address,
sizeof(address)) == 9);
398 CHECK(recv(udp, received,
sizeof(received), 0) == 9 && !strcmp(received,
"datagram"));
399 CHECK(close(sender) == 0 && close(udp) == 0 && close(listener) == 0);
403int namespace_network_test(
void) {
404 unsigned port = 34000 + (unsigned)getpid() % 20000;
405 int listener = inet_socket(SOCK_STREAM, port);
406 int udp = inet_socket(SOCK_DGRAM, port);
407 CHECK(listener >= 0 && udp >= 0);
408 struct sockaddr_un abstract = {.sun_family = AF_UNIX};
409 snprintf(abstract.sun_path + 1,
sizeof(abstract.sun_path) - 1,
"namespace-%ld", (
long)getpid());
410 socklen_t extent = offsetof(
struct sockaddr_un, sun_path) + 1 + strlen(abstract.sun_path + 1);
411 int named = socket(AF_UNIX, SOCK_STREAM, 0);
412 CHECK(named >= 0 && bind(named, (
struct sockaddr*)&abstract, extent) == 0);
413 CHECK(listen(named, 4) == 0);
414 pid_t child = clone_process(CLONE_NEWNET);
420 _exit(network_child(port, &abstract, extent));
424 CHECK(fcntl(udp, F_SETFL, O_NONBLOCK) == 0);
426 CHECK(recv(udp, received,
sizeof(received), 0) == -1 && errno == EAGAIN);
427 CHECK(close(listener) == 0 && close(udp) == 0 && close(named) == 0);
428 puts(
"NAMESPACE-CONTRACT: network PASS");
432int namespace_tests(
void) {
433 if (pid_namespace() || ipc_namespace() || mount_namespace() || namespace_network_test()) {
434 puts(
"NAMESPACE-CONTRACT: FAIL");
437 puts(
"NAMESPACE-CONTRACT: PASS");