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