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