Commit | Line | Data |
---|---|---|
4f5ce5e8 DH |
1 | #define _GNU_SOURCE |
2 | #define __EXPORTED_HEADERS__ | |
3 | ||
4 | #include <errno.h> | |
5 | #include <inttypes.h> | |
6 | #include <limits.h> | |
7 | #include <linux/falloc.h> | |
8 | #include <linux/fcntl.h> | |
9 | #include <linux/memfd.h> | |
10 | #include <sched.h> | |
11 | #include <stdio.h> | |
12 | #include <stdlib.h> | |
13 | #include <signal.h> | |
14 | #include <string.h> | |
15 | #include <sys/mman.h> | |
16 | #include <sys/stat.h> | |
17 | #include <sys/syscall.h> | |
18 | #include <unistd.h> | |
19 | ||
20 | #define MFD_DEF_SIZE 8192 | |
21 | #define STACK_SIZE 65535 | |
22 | ||
23 | static int sys_memfd_create(const char *name, | |
24 | unsigned int flags) | |
25 | { | |
26 | return syscall(__NR_memfd_create, name, flags); | |
27 | } | |
28 | ||
29 | static int mfd_assert_new(const char *name, loff_t sz, unsigned int flags) | |
30 | { | |
31 | int r, fd; | |
32 | ||
33 | fd = sys_memfd_create(name, flags); | |
34 | if (fd < 0) { | |
35 | printf("memfd_create(\"%s\", %u) failed: %m\n", | |
36 | name, flags); | |
37 | abort(); | |
38 | } | |
39 | ||
40 | r = ftruncate(fd, sz); | |
41 | if (r < 0) { | |
42 | printf("ftruncate(%llu) failed: %m\n", (unsigned long long)sz); | |
43 | abort(); | |
44 | } | |
45 | ||
46 | return fd; | |
47 | } | |
48 | ||
49 | static void mfd_fail_new(const char *name, unsigned int flags) | |
50 | { | |
51 | int r; | |
52 | ||
53 | r = sys_memfd_create(name, flags); | |
54 | if (r >= 0) { | |
55 | printf("memfd_create(\"%s\", %u) succeeded, but failure expected\n", | |
56 | name, flags); | |
57 | close(r); | |
58 | abort(); | |
59 | } | |
60 | } | |
61 | ||
57e67900 | 62 | static unsigned int mfd_assert_get_seals(int fd) |
4f5ce5e8 | 63 | { |
57e67900 | 64 | int r; |
4f5ce5e8 DH |
65 | |
66 | r = fcntl(fd, F_GET_SEALS); | |
67 | if (r < 0) { | |
68 | printf("GET_SEALS(%d) failed: %m\n", fd); | |
69 | abort(); | |
70 | } | |
71 | ||
57e67900 | 72 | return (unsigned int)r; |
4f5ce5e8 DH |
73 | } |
74 | ||
57e67900 | 75 | static void mfd_assert_has_seals(int fd, unsigned int seals) |
4f5ce5e8 | 76 | { |
57e67900 | 77 | unsigned int s; |
4f5ce5e8 DH |
78 | |
79 | s = mfd_assert_get_seals(fd); | |
80 | if (s != seals) { | |
57e67900 | 81 | printf("%u != %u = GET_SEALS(%d)\n", seals, s, fd); |
4f5ce5e8 DH |
82 | abort(); |
83 | } | |
84 | } | |
85 | ||
57e67900 | 86 | static void mfd_assert_add_seals(int fd, unsigned int seals) |
4f5ce5e8 | 87 | { |
57e67900 PK |
88 | int r; |
89 | unsigned int s; | |
4f5ce5e8 DH |
90 | |
91 | s = mfd_assert_get_seals(fd); | |
92 | r = fcntl(fd, F_ADD_SEALS, seals); | |
93 | if (r < 0) { | |
57e67900 | 94 | printf("ADD_SEALS(%d, %u -> %u) failed: %m\n", fd, s, seals); |
4f5ce5e8 DH |
95 | abort(); |
96 | } | |
97 | } | |
98 | ||
57e67900 | 99 | static void mfd_fail_add_seals(int fd, unsigned int seals) |
4f5ce5e8 | 100 | { |
57e67900 PK |
101 | int r; |
102 | unsigned int s; | |
4f5ce5e8 DH |
103 | |
104 | r = fcntl(fd, F_GET_SEALS); | |
105 | if (r < 0) | |
106 | s = 0; | |
107 | else | |
57e67900 | 108 | s = (unsigned int)r; |
4f5ce5e8 DH |
109 | |
110 | r = fcntl(fd, F_ADD_SEALS, seals); | |
111 | if (r >= 0) { | |
57e67900 PK |
112 | printf("ADD_SEALS(%d, %u -> %u) didn't fail as expected\n", |
113 | fd, s, seals); | |
4f5ce5e8 DH |
114 | abort(); |
115 | } | |
116 | } | |
117 | ||
118 | static void mfd_assert_size(int fd, size_t size) | |
119 | { | |
120 | struct stat st; | |
121 | int r; | |
122 | ||
123 | r = fstat(fd, &st); | |
124 | if (r < 0) { | |
125 | printf("fstat(%d) failed: %m\n", fd); | |
126 | abort(); | |
127 | } else if (st.st_size != size) { | |
128 | printf("wrong file size %lld, but expected %lld\n", | |
129 | (long long)st.st_size, (long long)size); | |
130 | abort(); | |
131 | } | |
132 | } | |
133 | ||
134 | static int mfd_assert_dup(int fd) | |
135 | { | |
136 | int r; | |
137 | ||
138 | r = dup(fd); | |
139 | if (r < 0) { | |
140 | printf("dup(%d) failed: %m\n", fd); | |
141 | abort(); | |
142 | } | |
143 | ||
144 | return r; | |
145 | } | |
146 | ||
147 | static void *mfd_assert_mmap_shared(int fd) | |
148 | { | |
149 | void *p; | |
150 | ||
151 | p = mmap(NULL, | |
152 | MFD_DEF_SIZE, | |
153 | PROT_READ | PROT_WRITE, | |
154 | MAP_SHARED, | |
155 | fd, | |
156 | 0); | |
157 | if (p == MAP_FAILED) { | |
158 | printf("mmap() failed: %m\n"); | |
159 | abort(); | |
160 | } | |
161 | ||
162 | return p; | |
163 | } | |
164 | ||
165 | static void *mfd_assert_mmap_private(int fd) | |
166 | { | |
167 | void *p; | |
168 | ||
169 | p = mmap(NULL, | |
170 | MFD_DEF_SIZE, | |
171 | PROT_READ, | |
172 | MAP_PRIVATE, | |
173 | fd, | |
174 | 0); | |
175 | if (p == MAP_FAILED) { | |
176 | printf("mmap() failed: %m\n"); | |
177 | abort(); | |
178 | } | |
179 | ||
180 | return p; | |
181 | } | |
182 | ||
183 | static int mfd_assert_open(int fd, int flags, mode_t mode) | |
184 | { | |
185 | char buf[512]; | |
186 | int r; | |
187 | ||
188 | sprintf(buf, "/proc/self/fd/%d", fd); | |
189 | r = open(buf, flags, mode); | |
190 | if (r < 0) { | |
191 | printf("open(%s) failed: %m\n", buf); | |
192 | abort(); | |
193 | } | |
194 | ||
195 | return r; | |
196 | } | |
197 | ||
198 | static void mfd_fail_open(int fd, int flags, mode_t mode) | |
199 | { | |
200 | char buf[512]; | |
201 | int r; | |
202 | ||
203 | sprintf(buf, "/proc/self/fd/%d", fd); | |
204 | r = open(buf, flags, mode); | |
205 | if (r >= 0) { | |
2ed36928 | 206 | printf("open(%s) didn't fail as expected\n", buf); |
4f5ce5e8 DH |
207 | abort(); |
208 | } | |
209 | } | |
210 | ||
211 | static void mfd_assert_read(int fd) | |
212 | { | |
213 | char buf[16]; | |
214 | void *p; | |
215 | ssize_t l; | |
216 | ||
217 | l = read(fd, buf, sizeof(buf)); | |
218 | if (l != sizeof(buf)) { | |
219 | printf("read() failed: %m\n"); | |
220 | abort(); | |
221 | } | |
222 | ||
223 | /* verify PROT_READ *is* allowed */ | |
224 | p = mmap(NULL, | |
225 | MFD_DEF_SIZE, | |
226 | PROT_READ, | |
227 | MAP_PRIVATE, | |
228 | fd, | |
229 | 0); | |
230 | if (p == MAP_FAILED) { | |
231 | printf("mmap() failed: %m\n"); | |
232 | abort(); | |
233 | } | |
234 | munmap(p, MFD_DEF_SIZE); | |
235 | ||
236 | /* verify MAP_PRIVATE is *always* allowed (even writable) */ | |
237 | p = mmap(NULL, | |
238 | MFD_DEF_SIZE, | |
239 | PROT_READ | PROT_WRITE, | |
240 | MAP_PRIVATE, | |
241 | fd, | |
242 | 0); | |
243 | if (p == MAP_FAILED) { | |
244 | printf("mmap() failed: %m\n"); | |
245 | abort(); | |
246 | } | |
247 | munmap(p, MFD_DEF_SIZE); | |
248 | } | |
249 | ||
250 | static void mfd_assert_write(int fd) | |
251 | { | |
252 | ssize_t l; | |
253 | void *p; | |
254 | int r; | |
255 | ||
256 | /* verify write() succeeds */ | |
257 | l = write(fd, "\0\0\0\0", 4); | |
258 | if (l != 4) { | |
259 | printf("write() failed: %m\n"); | |
260 | abort(); | |
261 | } | |
262 | ||
263 | /* verify PROT_READ | PROT_WRITE is allowed */ | |
264 | p = mmap(NULL, | |
265 | MFD_DEF_SIZE, | |
266 | PROT_READ | PROT_WRITE, | |
267 | MAP_SHARED, | |
268 | fd, | |
269 | 0); | |
270 | if (p == MAP_FAILED) { | |
271 | printf("mmap() failed: %m\n"); | |
272 | abort(); | |
273 | } | |
274 | *(char *)p = 0; | |
275 | munmap(p, MFD_DEF_SIZE); | |
276 | ||
277 | /* verify PROT_WRITE is allowed */ | |
278 | p = mmap(NULL, | |
279 | MFD_DEF_SIZE, | |
280 | PROT_WRITE, | |
281 | MAP_SHARED, | |
282 | fd, | |
283 | 0); | |
284 | if (p == MAP_FAILED) { | |
285 | printf("mmap() failed: %m\n"); | |
286 | abort(); | |
287 | } | |
288 | *(char *)p = 0; | |
289 | munmap(p, MFD_DEF_SIZE); | |
290 | ||
291 | /* verify PROT_READ with MAP_SHARED is allowed and a following | |
292 | * mprotect(PROT_WRITE) allows writing */ | |
293 | p = mmap(NULL, | |
294 | MFD_DEF_SIZE, | |
295 | PROT_READ, | |
296 | MAP_SHARED, | |
297 | fd, | |
298 | 0); | |
299 | if (p == MAP_FAILED) { | |
300 | printf("mmap() failed: %m\n"); | |
301 | abort(); | |
302 | } | |
303 | ||
304 | r = mprotect(p, MFD_DEF_SIZE, PROT_READ | PROT_WRITE); | |
305 | if (r < 0) { | |
306 | printf("mprotect() failed: %m\n"); | |
307 | abort(); | |
308 | } | |
309 | ||
310 | *(char *)p = 0; | |
311 | munmap(p, MFD_DEF_SIZE); | |
312 | ||
313 | /* verify PUNCH_HOLE works */ | |
314 | r = fallocate(fd, | |
315 | FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, | |
316 | 0, | |
317 | MFD_DEF_SIZE); | |
318 | if (r < 0) { | |
319 | printf("fallocate(PUNCH_HOLE) failed: %m\n"); | |
320 | abort(); | |
321 | } | |
322 | } | |
323 | ||
324 | static void mfd_fail_write(int fd) | |
325 | { | |
326 | ssize_t l; | |
327 | void *p; | |
328 | int r; | |
329 | ||
330 | /* verify write() fails */ | |
331 | l = write(fd, "data", 4); | |
332 | if (l != -EPERM) { | |
333 | printf("expected EPERM on write(), but got %d: %m\n", (int)l); | |
334 | abort(); | |
335 | } | |
336 | ||
337 | /* verify PROT_READ | PROT_WRITE is not allowed */ | |
338 | p = mmap(NULL, | |
339 | MFD_DEF_SIZE, | |
340 | PROT_READ | PROT_WRITE, | |
341 | MAP_SHARED, | |
342 | fd, | |
343 | 0); | |
344 | if (p != MAP_FAILED) { | |
345 | printf("mmap() didn't fail as expected\n"); | |
346 | abort(); | |
347 | } | |
348 | ||
349 | /* verify PROT_WRITE is not allowed */ | |
350 | p = mmap(NULL, | |
351 | MFD_DEF_SIZE, | |
352 | PROT_WRITE, | |
353 | MAP_SHARED, | |
354 | fd, | |
355 | 0); | |
356 | if (p != MAP_FAILED) { | |
357 | printf("mmap() didn't fail as expected\n"); | |
358 | abort(); | |
359 | } | |
360 | ||
361 | /* Verify PROT_READ with MAP_SHARED with a following mprotect is not | |
362 | * allowed. Note that for r/w the kernel already prevents the mmap. */ | |
363 | p = mmap(NULL, | |
364 | MFD_DEF_SIZE, | |
365 | PROT_READ, | |
366 | MAP_SHARED, | |
367 | fd, | |
368 | 0); | |
369 | if (p != MAP_FAILED) { | |
370 | r = mprotect(p, MFD_DEF_SIZE, PROT_READ | PROT_WRITE); | |
371 | if (r >= 0) { | |
372 | printf("mmap()+mprotect() didn't fail as expected\n"); | |
373 | abort(); | |
374 | } | |
375 | } | |
376 | ||
377 | /* verify PUNCH_HOLE fails */ | |
378 | r = fallocate(fd, | |
379 | FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, | |
380 | 0, | |
381 | MFD_DEF_SIZE); | |
382 | if (r >= 0) { | |
383 | printf("fallocate(PUNCH_HOLE) didn't fail as expected\n"); | |
384 | abort(); | |
385 | } | |
386 | } | |
387 | ||
388 | static void mfd_assert_shrink(int fd) | |
389 | { | |
390 | int r, fd2; | |
391 | ||
392 | r = ftruncate(fd, MFD_DEF_SIZE / 2); | |
393 | if (r < 0) { | |
394 | printf("ftruncate(SHRINK) failed: %m\n"); | |
395 | abort(); | |
396 | } | |
397 | ||
398 | mfd_assert_size(fd, MFD_DEF_SIZE / 2); | |
399 | ||
400 | fd2 = mfd_assert_open(fd, | |
401 | O_RDWR | O_CREAT | O_TRUNC, | |
402 | S_IRUSR | S_IWUSR); | |
403 | close(fd2); | |
404 | ||
405 | mfd_assert_size(fd, 0); | |
406 | } | |
407 | ||
408 | static void mfd_fail_shrink(int fd) | |
409 | { | |
410 | int r; | |
411 | ||
412 | r = ftruncate(fd, MFD_DEF_SIZE / 2); | |
413 | if (r >= 0) { | |
414 | printf("ftruncate(SHRINK) didn't fail as expected\n"); | |
415 | abort(); | |
416 | } | |
417 | ||
418 | mfd_fail_open(fd, | |
419 | O_RDWR | O_CREAT | O_TRUNC, | |
420 | S_IRUSR | S_IWUSR); | |
421 | } | |
422 | ||
423 | static void mfd_assert_grow(int fd) | |
424 | { | |
425 | int r; | |
426 | ||
427 | r = ftruncate(fd, MFD_DEF_SIZE * 2); | |
428 | if (r < 0) { | |
429 | printf("ftruncate(GROW) failed: %m\n"); | |
430 | abort(); | |
431 | } | |
432 | ||
433 | mfd_assert_size(fd, MFD_DEF_SIZE * 2); | |
434 | ||
435 | r = fallocate(fd, | |
436 | 0, | |
437 | 0, | |
438 | MFD_DEF_SIZE * 4); | |
439 | if (r < 0) { | |
440 | printf("fallocate(ALLOC) failed: %m\n"); | |
441 | abort(); | |
442 | } | |
443 | ||
444 | mfd_assert_size(fd, MFD_DEF_SIZE * 4); | |
445 | } | |
446 | ||
447 | static void mfd_fail_grow(int fd) | |
448 | { | |
449 | int r; | |
450 | ||
451 | r = ftruncate(fd, MFD_DEF_SIZE * 2); | |
452 | if (r >= 0) { | |
453 | printf("ftruncate(GROW) didn't fail as expected\n"); | |
454 | abort(); | |
455 | } | |
456 | ||
457 | r = fallocate(fd, | |
458 | 0, | |
459 | 0, | |
460 | MFD_DEF_SIZE * 4); | |
461 | if (r >= 0) { | |
462 | printf("fallocate(ALLOC) didn't fail as expected\n"); | |
463 | abort(); | |
464 | } | |
465 | } | |
466 | ||
467 | static void mfd_assert_grow_write(int fd) | |
468 | { | |
469 | static char buf[MFD_DEF_SIZE * 8]; | |
470 | ssize_t l; | |
471 | ||
472 | l = pwrite(fd, buf, sizeof(buf), 0); | |
473 | if (l != sizeof(buf)) { | |
474 | printf("pwrite() failed: %m\n"); | |
475 | abort(); | |
476 | } | |
477 | ||
478 | mfd_assert_size(fd, MFD_DEF_SIZE * 8); | |
479 | } | |
480 | ||
481 | static void mfd_fail_grow_write(int fd) | |
482 | { | |
483 | static char buf[MFD_DEF_SIZE * 8]; | |
484 | ssize_t l; | |
485 | ||
486 | l = pwrite(fd, buf, sizeof(buf), 0); | |
487 | if (l == sizeof(buf)) { | |
488 | printf("pwrite() didn't fail as expected\n"); | |
489 | abort(); | |
490 | } | |
491 | } | |
492 | ||
493 | static int idle_thread_fn(void *arg) | |
494 | { | |
495 | sigset_t set; | |
496 | int sig; | |
497 | ||
498 | /* dummy waiter; SIGTERM terminates us anyway */ | |
499 | sigemptyset(&set); | |
500 | sigaddset(&set, SIGTERM); | |
501 | sigwait(&set, &sig); | |
502 | ||
503 | return 0; | |
504 | } | |
505 | ||
506 | static pid_t spawn_idle_thread(unsigned int flags) | |
507 | { | |
508 | uint8_t *stack; | |
509 | pid_t pid; | |
510 | ||
511 | stack = malloc(STACK_SIZE); | |
512 | if (!stack) { | |
513 | printf("malloc(STACK_SIZE) failed: %m\n"); | |
514 | abort(); | |
515 | } | |
516 | ||
517 | pid = clone(idle_thread_fn, | |
518 | stack + STACK_SIZE, | |
519 | SIGCHLD | flags, | |
520 | NULL); | |
521 | if (pid < 0) { | |
522 | printf("clone() failed: %m\n"); | |
523 | abort(); | |
524 | } | |
525 | ||
526 | return pid; | |
527 | } | |
528 | ||
529 | static void join_idle_thread(pid_t pid) | |
530 | { | |
531 | kill(pid, SIGTERM); | |
532 | waitpid(pid, NULL, 0); | |
533 | } | |
534 | ||
535 | /* | |
536 | * Test memfd_create() syscall | |
537 | * Verify syscall-argument validation, including name checks, flag validation | |
538 | * and more. | |
539 | */ | |
540 | static void test_create(void) | |
541 | { | |
542 | char buf[2048]; | |
543 | int fd; | |
544 | ||
545 | /* test NULL name */ | |
546 | mfd_fail_new(NULL, 0); | |
547 | ||
548 | /* test over-long name (not zero-terminated) */ | |
549 | memset(buf, 0xff, sizeof(buf)); | |
550 | mfd_fail_new(buf, 0); | |
551 | ||
552 | /* test over-long zero-terminated name */ | |
553 | memset(buf, 0xff, sizeof(buf)); | |
554 | buf[sizeof(buf) - 1] = 0; | |
555 | mfd_fail_new(buf, 0); | |
556 | ||
557 | /* verify "" is a valid name */ | |
558 | fd = mfd_assert_new("", 0, 0); | |
559 | close(fd); | |
560 | ||
561 | /* verify invalid O_* open flags */ | |
562 | mfd_fail_new("", 0x0100); | |
563 | mfd_fail_new("", ~MFD_CLOEXEC); | |
564 | mfd_fail_new("", ~MFD_ALLOW_SEALING); | |
565 | mfd_fail_new("", ~0); | |
566 | mfd_fail_new("", 0x80000000U); | |
567 | ||
568 | /* verify MFD_CLOEXEC is allowed */ | |
569 | fd = mfd_assert_new("", 0, MFD_CLOEXEC); | |
570 | close(fd); | |
571 | ||
572 | /* verify MFD_ALLOW_SEALING is allowed */ | |
573 | fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING); | |
574 | close(fd); | |
575 | ||
576 | /* verify MFD_ALLOW_SEALING | MFD_CLOEXEC is allowed */ | |
577 | fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING | MFD_CLOEXEC); | |
578 | close(fd); | |
579 | } | |
580 | ||
581 | /* | |
582 | * Test basic sealing | |
583 | * A very basic sealing test to see whether setting/retrieving seals works. | |
584 | */ | |
585 | static void test_basic(void) | |
586 | { | |
587 | int fd; | |
588 | ||
589 | fd = mfd_assert_new("kern_memfd_basic", | |
590 | MFD_DEF_SIZE, | |
591 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | |
592 | ||
593 | /* add basic seals */ | |
594 | mfd_assert_has_seals(fd, 0); | |
595 | mfd_assert_add_seals(fd, F_SEAL_SHRINK | | |
596 | F_SEAL_WRITE); | |
597 | mfd_assert_has_seals(fd, F_SEAL_SHRINK | | |
598 | F_SEAL_WRITE); | |
599 | ||
600 | /* add them again */ | |
601 | mfd_assert_add_seals(fd, F_SEAL_SHRINK | | |
602 | F_SEAL_WRITE); | |
603 | mfd_assert_has_seals(fd, F_SEAL_SHRINK | | |
604 | F_SEAL_WRITE); | |
605 | ||
606 | /* add more seals and seal against sealing */ | |
607 | mfd_assert_add_seals(fd, F_SEAL_GROW | F_SEAL_SEAL); | |
608 | mfd_assert_has_seals(fd, F_SEAL_SHRINK | | |
609 | F_SEAL_GROW | | |
610 | F_SEAL_WRITE | | |
611 | F_SEAL_SEAL); | |
612 | ||
613 | /* verify that sealing no longer works */ | |
614 | mfd_fail_add_seals(fd, F_SEAL_GROW); | |
615 | mfd_fail_add_seals(fd, 0); | |
616 | ||
617 | close(fd); | |
618 | ||
619 | /* verify sealing does not work without MFD_ALLOW_SEALING */ | |
620 | fd = mfd_assert_new("kern_memfd_basic", | |
621 | MFD_DEF_SIZE, | |
622 | MFD_CLOEXEC); | |
623 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | |
624 | mfd_fail_add_seals(fd, F_SEAL_SHRINK | | |
625 | F_SEAL_GROW | | |
626 | F_SEAL_WRITE); | |
627 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | |
628 | close(fd); | |
629 | } | |
630 | ||
631 | /* | |
632 | * Test SEAL_WRITE | |
633 | * Test whether SEAL_WRITE actually prevents modifications. | |
634 | */ | |
635 | static void test_seal_write(void) | |
636 | { | |
637 | int fd; | |
638 | ||
639 | fd = mfd_assert_new("kern_memfd_seal_write", | |
640 | MFD_DEF_SIZE, | |
641 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | |
642 | mfd_assert_has_seals(fd, 0); | |
643 | mfd_assert_add_seals(fd, F_SEAL_WRITE); | |
644 | mfd_assert_has_seals(fd, F_SEAL_WRITE); | |
645 | ||
646 | mfd_assert_read(fd); | |
647 | mfd_fail_write(fd); | |
648 | mfd_assert_shrink(fd); | |
649 | mfd_assert_grow(fd); | |
650 | mfd_fail_grow_write(fd); | |
651 | ||
652 | close(fd); | |
653 | } | |
654 | ||
655 | /* | |
656 | * Test SEAL_SHRINK | |
657 | * Test whether SEAL_SHRINK actually prevents shrinking | |
658 | */ | |
659 | static void test_seal_shrink(void) | |
660 | { | |
661 | int fd; | |
662 | ||
663 | fd = mfd_assert_new("kern_memfd_seal_shrink", | |
664 | MFD_DEF_SIZE, | |
665 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | |
666 | mfd_assert_has_seals(fd, 0); | |
667 | mfd_assert_add_seals(fd, F_SEAL_SHRINK); | |
668 | mfd_assert_has_seals(fd, F_SEAL_SHRINK); | |
669 | ||
670 | mfd_assert_read(fd); | |
671 | mfd_assert_write(fd); | |
672 | mfd_fail_shrink(fd); | |
673 | mfd_assert_grow(fd); | |
674 | mfd_assert_grow_write(fd); | |
675 | ||
676 | close(fd); | |
677 | } | |
678 | ||
679 | /* | |
680 | * Test SEAL_GROW | |
681 | * Test whether SEAL_GROW actually prevents growing | |
682 | */ | |
683 | static void test_seal_grow(void) | |
684 | { | |
685 | int fd; | |
686 | ||
687 | fd = mfd_assert_new("kern_memfd_seal_grow", | |
688 | MFD_DEF_SIZE, | |
689 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | |
690 | mfd_assert_has_seals(fd, 0); | |
691 | mfd_assert_add_seals(fd, F_SEAL_GROW); | |
692 | mfd_assert_has_seals(fd, F_SEAL_GROW); | |
693 | ||
694 | mfd_assert_read(fd); | |
695 | mfd_assert_write(fd); | |
696 | mfd_assert_shrink(fd); | |
697 | mfd_fail_grow(fd); | |
698 | mfd_fail_grow_write(fd); | |
699 | ||
700 | close(fd); | |
701 | } | |
702 | ||
703 | /* | |
704 | * Test SEAL_SHRINK | SEAL_GROW | |
705 | * Test whether SEAL_SHRINK | SEAL_GROW actually prevents resizing | |
706 | */ | |
707 | static void test_seal_resize(void) | |
708 | { | |
709 | int fd; | |
710 | ||
711 | fd = mfd_assert_new("kern_memfd_seal_resize", | |
712 | MFD_DEF_SIZE, | |
713 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | |
714 | mfd_assert_has_seals(fd, 0); | |
715 | mfd_assert_add_seals(fd, F_SEAL_SHRINK | F_SEAL_GROW); | |
716 | mfd_assert_has_seals(fd, F_SEAL_SHRINK | F_SEAL_GROW); | |
717 | ||
718 | mfd_assert_read(fd); | |
719 | mfd_assert_write(fd); | |
720 | mfd_fail_shrink(fd); | |
721 | mfd_fail_grow(fd); | |
722 | mfd_fail_grow_write(fd); | |
723 | ||
724 | close(fd); | |
725 | } | |
726 | ||
727 | /* | |
728 | * Test sharing via dup() | |
729 | * Test that seals are shared between dupped FDs and they're all equal. | |
730 | */ | |
731 | static void test_share_dup(void) | |
732 | { | |
733 | int fd, fd2; | |
734 | ||
735 | fd = mfd_assert_new("kern_memfd_share_dup", | |
736 | MFD_DEF_SIZE, | |
737 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | |
738 | mfd_assert_has_seals(fd, 0); | |
739 | ||
740 | fd2 = mfd_assert_dup(fd); | |
741 | mfd_assert_has_seals(fd2, 0); | |
742 | ||
743 | mfd_assert_add_seals(fd, F_SEAL_WRITE); | |
744 | mfd_assert_has_seals(fd, F_SEAL_WRITE); | |
745 | mfd_assert_has_seals(fd2, F_SEAL_WRITE); | |
746 | ||
747 | mfd_assert_add_seals(fd2, F_SEAL_SHRINK); | |
748 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK); | |
749 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK); | |
750 | ||
751 | mfd_assert_add_seals(fd, F_SEAL_SEAL); | |
752 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL); | |
753 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL); | |
754 | ||
755 | mfd_fail_add_seals(fd, F_SEAL_GROW); | |
756 | mfd_fail_add_seals(fd2, F_SEAL_GROW); | |
757 | mfd_fail_add_seals(fd, F_SEAL_SEAL); | |
758 | mfd_fail_add_seals(fd2, F_SEAL_SEAL); | |
759 | ||
760 | close(fd2); | |
761 | ||
762 | mfd_fail_add_seals(fd, F_SEAL_GROW); | |
763 | close(fd); | |
764 | } | |
765 | ||
766 | /* | |
767 | * Test sealing with active mmap()s | |
768 | * Modifying seals is only allowed if no other mmap() refs exist. | |
769 | */ | |
770 | static void test_share_mmap(void) | |
771 | { | |
772 | int fd; | |
773 | void *p; | |
774 | ||
775 | fd = mfd_assert_new("kern_memfd_share_mmap", | |
776 | MFD_DEF_SIZE, | |
777 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | |
778 | mfd_assert_has_seals(fd, 0); | |
779 | ||
780 | /* shared/writable ref prevents sealing WRITE, but allows others */ | |
781 | p = mfd_assert_mmap_shared(fd); | |
782 | mfd_fail_add_seals(fd, F_SEAL_WRITE); | |
783 | mfd_assert_has_seals(fd, 0); | |
784 | mfd_assert_add_seals(fd, F_SEAL_SHRINK); | |
785 | mfd_assert_has_seals(fd, F_SEAL_SHRINK); | |
786 | munmap(p, MFD_DEF_SIZE); | |
787 | ||
788 | /* readable ref allows sealing */ | |
789 | p = mfd_assert_mmap_private(fd); | |
790 | mfd_assert_add_seals(fd, F_SEAL_WRITE); | |
791 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK); | |
792 | munmap(p, MFD_DEF_SIZE); | |
793 | ||
794 | close(fd); | |
795 | } | |
796 | ||
797 | /* | |
798 | * Test sealing with open(/proc/self/fd/%d) | |
799 | * Via /proc we can get access to a separate file-context for the same memfd. | |
800 | * This is *not* like dup(), but like a real separate open(). Make sure the | |
801 | * semantics are as expected and we correctly check for RDONLY / WRONLY / RDWR. | |
802 | */ | |
803 | static void test_share_open(void) | |
804 | { | |
805 | int fd, fd2; | |
806 | ||
807 | fd = mfd_assert_new("kern_memfd_share_open", | |
808 | MFD_DEF_SIZE, | |
809 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | |
810 | mfd_assert_has_seals(fd, 0); | |
811 | ||
812 | fd2 = mfd_assert_open(fd, O_RDWR, 0); | |
813 | mfd_assert_add_seals(fd, F_SEAL_WRITE); | |
814 | mfd_assert_has_seals(fd, F_SEAL_WRITE); | |
815 | mfd_assert_has_seals(fd2, F_SEAL_WRITE); | |
816 | ||
817 | mfd_assert_add_seals(fd2, F_SEAL_SHRINK); | |
818 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK); | |
819 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK); | |
820 | ||
821 | close(fd); | |
822 | fd = mfd_assert_open(fd2, O_RDONLY, 0); | |
823 | ||
824 | mfd_fail_add_seals(fd, F_SEAL_SEAL); | |
825 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK); | |
826 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK); | |
827 | ||
828 | close(fd2); | |
829 | fd2 = mfd_assert_open(fd, O_RDWR, 0); | |
830 | ||
831 | mfd_assert_add_seals(fd2, F_SEAL_SEAL); | |
832 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL); | |
833 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL); | |
834 | ||
835 | close(fd2); | |
836 | close(fd); | |
837 | } | |
838 | ||
839 | /* | |
840 | * Test sharing via fork() | |
841 | * Test whether seal-modifications work as expected with forked childs. | |
842 | */ | |
843 | static void test_share_fork(void) | |
844 | { | |
845 | int fd; | |
846 | pid_t pid; | |
847 | ||
848 | fd = mfd_assert_new("kern_memfd_share_fork", | |
849 | MFD_DEF_SIZE, | |
850 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | |
851 | mfd_assert_has_seals(fd, 0); | |
852 | ||
853 | pid = spawn_idle_thread(0); | |
854 | mfd_assert_add_seals(fd, F_SEAL_SEAL); | |
855 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | |
856 | ||
857 | mfd_fail_add_seals(fd, F_SEAL_WRITE); | |
858 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | |
859 | ||
860 | join_idle_thread(pid); | |
861 | ||
862 | mfd_fail_add_seals(fd, F_SEAL_WRITE); | |
863 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | |
864 | ||
865 | close(fd); | |
866 | } | |
867 | ||
868 | int main(int argc, char **argv) | |
869 | { | |
870 | pid_t pid; | |
871 | ||
872 | printf("memfd: CREATE\n"); | |
873 | test_create(); | |
874 | printf("memfd: BASIC\n"); | |
875 | test_basic(); | |
876 | ||
877 | printf("memfd: SEAL-WRITE\n"); | |
878 | test_seal_write(); | |
879 | printf("memfd: SEAL-SHRINK\n"); | |
880 | test_seal_shrink(); | |
881 | printf("memfd: SEAL-GROW\n"); | |
882 | test_seal_grow(); | |
883 | printf("memfd: SEAL-RESIZE\n"); | |
884 | test_seal_resize(); | |
885 | ||
886 | printf("memfd: SHARE-DUP\n"); | |
887 | test_share_dup(); | |
888 | printf("memfd: SHARE-MMAP\n"); | |
889 | test_share_mmap(); | |
890 | printf("memfd: SHARE-OPEN\n"); | |
891 | test_share_open(); | |
892 | printf("memfd: SHARE-FORK\n"); | |
893 | test_share_fork(); | |
894 | ||
895 | /* Run test-suite in a multi-threaded environment with a shared | |
896 | * file-table. */ | |
897 | pid = spawn_idle_thread(CLONE_FILES | CLONE_FS | CLONE_VM); | |
898 | printf("memfd: SHARE-DUP (shared file-table)\n"); | |
899 | test_share_dup(); | |
900 | printf("memfd: SHARE-MMAP (shared file-table)\n"); | |
901 | test_share_mmap(); | |
902 | printf("memfd: SHARE-OPEN (shared file-table)\n"); | |
903 | test_share_open(); | |
904 | printf("memfd: SHARE-FORK (shared file-table)\n"); | |
905 | test_share_fork(); | |
906 | join_idle_thread(pid); | |
907 | ||
908 | printf("memfd: DONE\n"); | |
909 | ||
910 | return 0; | |
911 | } |