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