The Pedigree Project 0.1
memfd-contract-test/lifetime.c
1#define _GNU_SOURCE
2#include <signal.h>
3#include <stdlib.h>
4#include <string.h>
5#include <unistd.h>
6
7#include "contract.h"
8#include <sys/mman.h>
9#include <sys/socket.h>
10
11static int fork_seals(void) {
12 int failed = 0, fd = -1, alias = -1, pair[2] = {-1, -1};
13 pid_t child = -1;
14 CHECK((fd = mf_make(32)) >= 0 && (alias = dup(fd)) >= 0);
15 CHECK(pwrite(fd, "a", 1, 0) == 1);
16 CHECK(!socketpair(AF_UNIX, SOCK_STREAM, 0, pair));
17 CHECK((child = fork()) >= 0);
18 if (!child) {
19 alarm(8);
20 close(pair[0]);
21 close(fd);
22 if (write(pair[1], "r", 1) != 1 || mf_byte(pair[1], 'g'))
23 _exit(10);
24 if (fcntl(alias, F_GET_SEALS) != F_SEAL_GROW || fcntl(alias, F_ADD_SEALS, F_SEAL_SHRINK) ||
25 pwrite(alias, "b", 1, 0) != 1)
26 _exit(11);
27 _exit(write(pair[1], "s", 1) == 1 ? 0 : 12);
28 }
29 close(pair[1]);
30 pair[1] = -1;
31 CHECK(!mf_byte(pair[0], 'r'));
32 CHECK(!fcntl(fd, F_ADD_SEALS, F_SEAL_GROW));
33 CHECK(write(pair[0], "g", 1) == 1 && !mf_byte(pair[0], 's'));
34 CHECK(fcntl(fd, F_GET_SEALS) == (F_SEAL_GROW | F_SEAL_SHRINK));
35 CHECK(fcntl(alias, F_GET_SEALS) == (F_SEAL_GROW | F_SEAL_SHRINK));
36 CHECK(!mf_contents(fd, 0, "b", 1));
37 const int status = mf_reap(child, 5000);
38 child = -1;
39 CHECK(!status);
40out:
41 if (child > 0) {
42 kill(child, SIGKILL);
43 mf_reap(child, 1000);
44 }
45 if (pair[0] >= 0)
46 close(pair[0]);
47 if (pair[1] >= 0)
48 close(pair[1]);
49 if (alias >= 0)
50 close(alias);
51 if (fd >= 0)
52 close(fd);
53 return failed;
54}
55static int queued_rights(void) {
56 int failed = 0, fd = -1, received = -1, pair[2] = {-1, -1}, gate[2] = {-1, -1};
57 pid_t child = -1;
58 CHECK((fd = mf_make(32)) >= 0 && pwrite(fd, "queue", 5, 0) == 5);
59 CHECK(!fcntl(fd, F_ADD_SEALS, F_SEAL_GROW));
60 CHECK(!socketpair(AF_UNIX, SOCK_STREAM, 0, pair) && !socketpair(AF_UNIX, SOCK_STREAM, 0, gate));
61 CHECK((child = fork()) >= 0);
62 if (!child) {
63 alarm(8);
64 close(pair[0]);
65 close(gate[0]);
66 close(fd);
67 if (write(gate[1], "r", 1) != 1 || mf_byte(gate[1], 'g'))
68 _exit(20);
69 int transferred = mf_receive_fd(pair[1]);
70 if (transferred < 0 || fcntl(transferred, F_GETFD) != FD_CLOEXEC ||
71 fcntl(transferred, F_GET_SEALS) != F_SEAL_GROW || mf_contents(transferred, 0, "queue", 5) ||
72 fcntl(transferred, F_ADD_SEALS, F_SEAL_SHRINK) || mf_send_fd(pair[1], transferred))
73 _exit(21);
74 close(transferred);
75 _exit(write(gate[1], "s", 1) == 1 ? 0 : 22);
76 }
77 close(pair[1]);
78 pair[1] = -1;
79 close(gate[1]);
80 gate[1] = -1;
81 CHECK(!mf_byte(gate[0], 'r') && !mf_send_fd(pair[0], fd));
82 CHECK(!close(fd));
83 fd = -1;
84 /* The child cannot receive until only the queued rights keep the file alive. */
85 CHECK(write(gate[0], "g", 1) == 1 && !mf_byte(gate[0], 's'));
86 CHECK((received = mf_receive_fd(pair[0])) >= 0);
87 CHECK(fcntl(received, F_GET_SEALS) == (F_SEAL_GROW | F_SEAL_SHRINK));
88 CHECK(!mf_contents(received, 0, "queue", 5));
89 CHECK(ftruncate(received, 31) == -1 && errno == EPERM);
90 const int status = mf_reap(child, 5000);
91 child = -1;
92 CHECK(!status);
93out:
94 if (child > 0) {
95 kill(child, SIGKILL);
96 mf_reap(child, 1000);
97 }
98 for (int n = 0; n < 2; ++n) {
99 if (pair[n] >= 0)
100 close(pair[n]);
101 if (gate[n] >= 0)
102 close(gate[n]);
103 }
104 if (received >= 0)
105 close(received);
106 if (fd >= 0)
107 close(fd);
108 return failed;
109}
110static int child_capability(int explicit_unmap) {
111 int failed = 0, fd = -1, pair[2] = {-1, -1};
112 const size_t page = sysconf(_SC_PAGESIZE);
113 char* mapping = MAP_FAILED;
114 pid_t child = -1;
115 CHECK((fd = mf_make(page)) >= 0);
116 mapping = mmap(NULL, page, PROT_READ, MAP_SHARED, fd, 0);
117 CHECK(mapping != MAP_FAILED && !socketpair(AF_UNIX, SOCK_STREAM, 0, pair));
118 CHECK((child = fork()) >= 0);
119 if (!child) {
120 alarm(8);
121 close(pair[0]);
122 close(fd);
123 if (write(pair[1], "r", 1) != 1 || mf_byte(pair[1], 'g') ||
124 mprotect(mapping, page, PROT_READ | PROT_WRITE))
125 _exit(30);
126 mapping[0] = 'c';
127 if (write(pair[1], "w", 1) != 1 || mf_byte(pair[1], 'u'))
128 _exit(31);
129 if (explicit_unmap &&
130 (munmap(mapping, page) || write(pair[1], "u", 1) != 1 || mf_byte(pair[1], 'q')))
131 _exit(32);
132 _exit(0);
133 }
134 close(pair[1]);
135 pair[1] = -1;
136 CHECK(!mf_byte(pair[0], 'r'));
137 CHECK(!munmap(mapping, page));
138 mapping = MAP_FAILED;
139 CHECK(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_SHRINK) == -1 && errno == EBUSY);
140 CHECK(fcntl(fd, F_GET_SEALS) == 0 && !fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE));
141 CHECK(write(pair[0], "g", 1) == 1 && !mf_byte(pair[0], 'w'));
142 CHECK(!mf_contents(fd, 0, "c", 1));
143 CHECK(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1 && errno == EBUSY);
144 CHECK(write(pair[0], "u", 1) == 1);
145 if (explicit_unmap) {
146 CHECK(!mf_byte(pair[0], 'u'));
147 CHECK(!fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE));
148 CHECK(write(pair[0], "q", 1) == 1);
149 }
150 const int status = mf_reap(child, 5000);
151 child = -1;
152 CHECK(!status);
153 CHECK(!fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE));
154 CHECK(fcntl(fd, F_GET_SEALS) == (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE));
155out:
156 if (child > 0) {
157 kill(child, SIGKILL);
158 mf_reap(child, 1000);
159 }
160 if (pair[0] >= 0)
161 close(pair[0]);
162 if (pair[1] >= 0)
163 close(pair[1]);
164 if (mapping != MAP_FAILED)
165 munmap(mapping, page);
166 if (fd >= 0)
167 close(fd);
168 return failed;
169}
170static int mapped_last_close(void) {
171 int failed = 0, fd = -1;
172 const size_t page = sysconf(_SC_PAGESIZE);
173 char *mapping = MAP_FAILED, *alias = MAP_FAILED, *target = MAP_FAILED, *private = MAP_FAILED;
174 size_t length = 2 * page;
175 pid_t child = -1;
176 CHECK((fd = mf_make(3 * page)) >= 0 && pwrite(fd, "last", 4, 0) == 4);
177 mapping = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
178 alias = mmap(NULL, page, PROT_READ, MAP_SHARED, fd, 0);
179 CHECK(mapping != MAP_FAILED && alias != MAP_FAILED);
180 CHECK(!fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE));
181 private = mmap(NULL, page, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
182 CHECK(private != MAP_FAILED);
183 private[0] = 'p';
184 CHECK(!close(fd));
185 fd = -1;
186 CHECK(!memcmp(alias, "last", 4));
187 target = mmap(NULL, 3 * page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
188 CHECK(target != MAP_FAILED);
189 char* moved = mremap(mapping, length, 3 * page, MREMAP_MAYMOVE | MREMAP_FIXED, target);
190 CHECK(moved == target);
191 mapping = moved;
192 length = 3 * page;
193 target = MAP_FAILED;
194 CHECK(!madvise(mapping, length, MADV_DONTNEED) && !memcmp(mapping, "last", 4));
195 mapping[0] = 'L';
196 mapping[2 * page] = 'g';
197 CHECK(alias[0] == 'L');
198 CHECK(!madvise(mapping, length, MADV_DONTNEED));
199 CHECK(mapping[0] == 'L' && mapping[2 * page] == 'g');
200 CHECK((child = fork()) >= 0);
201 if (!child) {
202 alarm(8);
203 if (private[0] != 'p' || mapping[0] != 'L' || mapping[2 * page] != 'g')
204 _exit(35);
205 private[0] = 'c';
206 mapping[0] = 'F';
207 _exit(private[0] == 'c' && alias[0] == 'F' ? 0 : 36);
208 }
209 const int status = mf_reap(child, 5000);
210 child = -1;
211 CHECK(!status && private[0] == 'p' && alias[0] == 'F' && mapping[0] == 'F');
212out:
213 if (child > 0) {
214 kill(child, SIGKILL);
215 mf_reap(child, 1000);
216 }
217 if (private != MAP_FAILED)
218 munmap(private, page);
219 if (target != MAP_FAILED)
220 munmap(target, 3 * page);
221 if (alias != MAP_FAILED)
222 munmap(alias, page);
223 if (mapping != MAP_FAILED)
224 munmap(mapping, length);
225 if (fd >= 0)
226 close(fd);
227 return failed;
228}
229int memfd_exec(int argc, char** argv) {
230 if (argc != 4)
231 return 40;
232 const int closed = atoi(argv[2]), retained = atoi(argv[3]);
233 if (fcntl(closed, F_GETFD) != -1 || errno != EBADF || fcntl(retained, F_GETFD) != 0 ||
234 fcntl(retained, F_GET_SEALS) != (F_SEAL_GROW | F_SEAL_SHRINK) ||
235 mf_contents(retained, 0, "exec", 4))
236 return 41;
237 if (fcntl(retained, F_ADD_SEALS, F_SEAL_WRITE))
238 return 42;
239 return close(retained) ? 43 : 0;
240}
241static int exec_lifetime(void) {
242 int failed = 0, fd = -1, alias = -1;
243 pid_t child = -1;
244 char closed[24], retained[24];
245 CHECK((fd = memfd_create("exec", MFD_ALLOW_SEALING | MFD_CLOEXEC)) >= 0);
246 CHECK(write(fd, "exec", 4) == 4 && (alias = dup(fd)) >= 0);
247 CHECK(!fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK));
248 snprintf(closed, sizeof(closed), "%d", fd);
249 snprintf(retained, sizeof(retained), "%d", alias);
250 CHECK((child = fork()) >= 0);
251 if (!child) {
252 alarm(8);
253 execl(MEMFD_APP, MEMFD_APP, "memfd-exec", closed, retained, (char*)NULL);
254 _exit(44);
255 }
256 const int status = mf_reap(child, 6000);
257 child = -1;
258 CHECK(!status);
259 CHECK(fcntl(fd, F_GET_SEALS) == (F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_WRITE));
260out:
261 if (child > 0) {
262 kill(child, SIGKILL);
263 mf_reap(child, 1000);
264 }
265 if (alias >= 0)
266 close(alias);
267 if (fd >= 0)
268 close(fd);
269 return failed;
270}
271int memfd_lifetime(void) {
272 return fork_seals() || queued_rights() || child_capability(1) || child_capability(0) ||
273 mapped_last_close() || exec_lifetime();
274}