The Pedigree Project 0.1
uts-namespace-contract-test/lifetime.c
1#define _GNU_SOURCE
2#include <fcntl.h>
3#include <pthread.h>
4#include <sched.h>
5#include <signal.h>
6#include <stdlib.h>
7#include <string.h>
8#include <unistd.h>
9
10#include "contract.h"
11#include <sys/mman.h>
12#include <sys/socket.h>
13
15 int socket;
16};
17static int send_namespace(int command, int report, void* argument) {
18 (void)command;
19 struct rights_sender* state = argument;
20 if (unshare(CLONE_NEWUTS) || ns_set("orphan-namespace", "orphan-domain"))
21 return 1;
22 int fd = open("/proc/thread-self/ns/uts", O_RDONLY | O_CLOEXEC);
23 struct ns_identity identity;
24 if (fd < 0 || ns_fd_identity(fd, &identity) || ns_write(report, &identity, sizeof(identity)) ||
25 ns_send_fd(state->socket, fd)) {
26 if (fd >= 0)
27 close(fd);
28 return 1;
29 }
30 close(fd);
31 close(state->socket);
32 return 0;
33}
34
35static int join_inherited(int command, int report, void* argument) {
36 int fd = *(int*)argument;
37 if (ns_send(report, 'R') || ns_receive(command, 'J') || setns(fd, CLONE_NEWUTS) ||
38 ns_expect("orphan-namespace", "orphan-domain"))
39 return 1;
40 close(fd);
41 return ns_expect("orphan-namespace", "orphan-domain");
42}
43
44static int retained_descriptors(void) {
45 int failed = 0, socket[2] = {-1, -1}, fd = -1, alias = -1, regular = -1;
46 struct ns_peer source = NS_PEER_INITIALIZER, child = NS_PEER_INITIALIZER;
47 struct ns_identity expected, actual, original, current;
48 CHECK(ns_set("rights-parent", "rights-parent") == 0);
49 CHECK(ns_path_identity("/proc/thread-self/ns/uts", &original) == 0);
50 CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, socket) == 0);
51 struct rights_sender sender = {socket[1]};
52 CHECK(ns_spawn(&source, send_namespace, &sender) == 0);
53 close(socket[1]);
54 socket[1] = -1;
55 CHECK(ns_read(source.report, &expected, sizeof(expected)) == 0);
56 CHECK(ns_join(&source) == 0);
57 /* The source task and its descriptor are gone before rights are received. */
58 fd = ns_receive_fd(socket[0]);
59 CHECK(fd >= 0 && ns_fd_identity(fd, &actual) == 0 && ns_same(actual, expected));
60 alias = dup(fd);
61 CHECK(alias >= 0);
62 close(fd);
63 fd = -1;
64 CHECK(ns_fd_identity(alias, &actual) == 0 && ns_same(actual, expected));
65 CHECK(ns_spawn(&child, join_inherited, &alias) == 0 && ns_receive(child.report, 'R') == 0);
66 int reused = alias;
67 close(alias);
68 alias = -1;
69 regular = memfd_create("namespace-fd-reuse", MFD_CLOEXEC);
70 CHECK(regular >= 0);
71 if (regular != reused) {
72 CHECK(dup2(regular, reused) == reused);
73 close(regular);
74 regular = reused;
75 }
76 errno = 0;
77 CHECK(setns(regular, 0) == -1 && errno == EINVAL);
78 CHECK(ns_path_identity("/proc/thread-self/ns/uts", &current) == 0 && ns_same(current, original));
79 CHECK(ns_send(child.command, 'J') == 0 && ns_join(&child) == 0);
80 CHECK(ns_expect("rights-parent", "rights-parent") == 0);
81out:
82 ns_cleanup(&child);
83 ns_cleanup(&source);
84 if (regular >= 0)
85 close(regular);
86 if (alias >= 0)
87 close(alias);
88 if (fd >= 0)
89 close(fd);
90 for (int i = 0; i < 2; ++i)
91 if (socket[i] >= 0)
92 close(socket[i]);
93 return failed;
94}
95
96int ns_exec(int argc, char** argv) {
97 int failed = 0;
98 alarm(15);
99 CHECK(argc == 6);
100 int retained = atoi(argv[4]), closed = atoi(argv[5]);
101 struct ns_identity own, saved, leader;
102 CHECK(ns_expect(argv[2], argv[3]) == 0);
103 CHECK(ns_fd_identity(retained, &saved) == 0);
104 CHECK(ns_path_identity("/proc/thread-self/ns/uts", &own) == 0 && ns_same(saved, own));
105 CHECK(ns_path_identity("/proc/self/ns/uts", &leader) == 0 && ns_same(leader, own));
106 errno = 0;
107 CHECK(fcntl(closed, F_GETFD) == -1 && errno == EBADF);
108 CHECK(!(fcntl(retained, F_GETFD) & FD_CLOEXEC));
109 CHECK(unshare(CLONE_NEWUTS) == 0 && ns_set("exec-temporary", "exec-temporary") == 0);
110 CHECK(setns(retained, 0) == 0 && ns_expect(argv[2], argv[3]) == 0);
111 close(retained);
112 CHECK(ns_expect(argv[2], argv[3]) == 0);
113out:
114 return failed;
115}
116
117static int enter_exec(const char* host) {
118 if (ns_set(host, "exec-domain"))
119 return 1;
120 int original = open("/proc/thread-self/ns/uts", O_RDONLY | O_CLOEXEC);
121 if (original < 0)
122 return 1;
123 int retained = fcntl(original, F_DUPFD, 100);
124 int closed = fcntl(original, F_DUPFD_CLOEXEC, 200);
125 close(original);
126 if (retained < 0 || closed < 0)
127 return 1;
128 char retained_text[16], closed_text[16];
129 snprintf(retained_text, sizeof(retained_text), "%d", retained);
130 snprintf(closed_text, sizeof(closed_text), "%d", closed);
131 execl("/applications/uts-namespace-no-such-executable", "absent", (char*)NULL);
132 if (errno != ENOENT || ns_expect(host, "exec-domain"))
133 return 1;
134 execl(NS_APP, NS_APP, "uts-exec", host, "exec-domain", retained_text, closed_text, (char*)NULL);
135 return 1;
136}
137static void* exec_worker(void* argument) {
138 (void)argument;
139 if (unshare(CLONE_NEWUTS))
140 _exit(2);
141 _exit(enter_exec("exec-worker") ? 3 : 0);
142}
143static int exec_child(int command, int report, void* argument) {
144 (void)command;
145 (void)report;
146 if (unshare(CLONE_NEWUTS))
147 return 1;
148 if (!*(int*)argument)
149 return enter_exec("exec-leader");
150 pthread_t worker;
151 if (ns_set("old-exec-leader", "old-exec-leader") ||
152 pthread_create(&worker, NULL, exec_worker, NULL))
153 return 1;
154 for (;;)
155 pause();
156}
157static int exec_membership(void) {
158 int failed = 0;
159 struct ns_peer child = NS_PEER_INITIALIZER;
160 for (int threaded = 0; threaded < 2; ++threaded) {
161 CHECK(ns_spawn(&child, exec_child, &threaded) == 0);
162 CHECK(ns_join(&child) == 0);
163 }
164out:
165 ns_cleanup(&child);
166 return failed;
167}
168
169int ns_lifetime(void) {
170 return retained_descriptors() || exec_membership();
171}