• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <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 
mfd_assert_new(const char * name,loff_t sz,unsigned int flags)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 
mfd_assert_reopen_fd(int fd_in)57 static int mfd_assert_reopen_fd(int fd_in)
58 {
59 	int 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 
mfd_fail_new(const char * name,unsigned int flags)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 
mfd_assert_get_seals(int fd)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 
mfd_assert_has_seals(int fd,unsigned int seals)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 
mfd_assert_add_seals(int fd,unsigned int seals)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 
mfd_fail_add_seals(int fd,unsigned int seals)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 
mfd_assert_size(int fd,size_t size)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 
mfd_assert_dup(int fd)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 
mfd_assert_mmap_shared(int fd)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 
mfd_assert_mmap_private(int fd)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 
mfd_assert_open(int fd,int flags,mode_t mode)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 
mfd_fail_open(int fd,int flags,mode_t mode)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 
mfd_assert_read(int fd)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. */
mfd_assert_read_shared(int fd)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 
mfd_assert_fork_private_write(int fd)293 static void mfd_assert_fork_private_write(int fd)
294 {
295 	int *p;
296 	pid_t pid;
297 
298 	p = mmap(NULL,
299 		 mfd_def_size,
300 		 PROT_READ | PROT_WRITE,
301 		 MAP_PRIVATE,
302 		 fd,
303 		 0);
304 	if (p == MAP_FAILED) {
305 		printf("mmap() failed: %m\n");
306 		abort();
307 	}
308 
309 	p[0] = 22;
310 
311 	pid = fork();
312 	if (pid == 0) {
313 		p[0] = 33;
314 		exit(0);
315 	} else {
316 		waitpid(pid, NULL, 0);
317 
318 		if (p[0] != 22) {
319 			printf("MAP_PRIVATE copy-on-write failed: %m\n");
320 			abort();
321 		}
322 	}
323 
324 	munmap(p, mfd_def_size);
325 }
326 
mfd_assert_write(int fd)327 static void mfd_assert_write(int fd)
328 {
329 	ssize_t l;
330 	void *p;
331 	int r;
332 
333 	/*
334 	 * huegtlbfs does not support write, but we want to
335 	 * verify everything else here.
336 	 */
337 	if (!hugetlbfs_test) {
338 		/* verify write() succeeds */
339 		l = write(fd, "\0\0\0\0", 4);
340 		if (l != 4) {
341 			printf("write() failed: %m\n");
342 			abort();
343 		}
344 	}
345 
346 	/* verify PROT_READ | PROT_WRITE is allowed */
347 	p = mmap(NULL,
348 		 mfd_def_size,
349 		 PROT_READ | PROT_WRITE,
350 		 MAP_SHARED,
351 		 fd,
352 		 0);
353 	if (p == MAP_FAILED) {
354 		printf("mmap() failed: %m\n");
355 		abort();
356 	}
357 	*(char *)p = 0;
358 	munmap(p, mfd_def_size);
359 
360 	/* verify PROT_WRITE is allowed */
361 	p = mmap(NULL,
362 		 mfd_def_size,
363 		 PROT_WRITE,
364 		 MAP_SHARED,
365 		 fd,
366 		 0);
367 	if (p == MAP_FAILED) {
368 		printf("mmap() failed: %m\n");
369 		abort();
370 	}
371 	*(char *)p = 0;
372 	munmap(p, mfd_def_size);
373 
374 	/* verify PROT_READ with MAP_SHARED is allowed and a following
375 	 * mprotect(PROT_WRITE) allows writing */
376 	p = mmap(NULL,
377 		 mfd_def_size,
378 		 PROT_READ,
379 		 MAP_SHARED,
380 		 fd,
381 		 0);
382 	if (p == MAP_FAILED) {
383 		printf("mmap() failed: %m\n");
384 		abort();
385 	}
386 
387 	r = mprotect(p, mfd_def_size, PROT_READ | PROT_WRITE);
388 	if (r < 0) {
389 		printf("mprotect() failed: %m\n");
390 		abort();
391 	}
392 
393 	*(char *)p = 0;
394 	munmap(p, mfd_def_size);
395 
396 	/* verify PUNCH_HOLE works */
397 	r = fallocate(fd,
398 		      FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
399 		      0,
400 		      mfd_def_size);
401 	if (r < 0) {
402 		printf("fallocate(PUNCH_HOLE) failed: %m\n");
403 		abort();
404 	}
405 }
406 
mfd_fail_write(int fd)407 static void mfd_fail_write(int fd)
408 {
409 	ssize_t l;
410 	void *p;
411 	int r;
412 
413 	/* verify write() fails */
414 	l = write(fd, "data", 4);
415 	if (l != -EPERM) {
416 		printf("expected EPERM on write(), but got %d: %m\n", (int)l);
417 		abort();
418 	}
419 
420 	/* verify PROT_READ | PROT_WRITE is not allowed */
421 	p = mmap(NULL,
422 		 mfd_def_size,
423 		 PROT_READ | PROT_WRITE,
424 		 MAP_SHARED,
425 		 fd,
426 		 0);
427 	if (p != MAP_FAILED) {
428 		printf("mmap() didn't fail as expected\n");
429 		abort();
430 	}
431 
432 	/* verify PROT_WRITE is not allowed */
433 	p = mmap(NULL,
434 		 mfd_def_size,
435 		 PROT_WRITE,
436 		 MAP_SHARED,
437 		 fd,
438 		 0);
439 	if (p != MAP_FAILED) {
440 		printf("mmap() didn't fail as expected\n");
441 		abort();
442 	}
443 
444 	/* Verify PROT_READ with MAP_SHARED with a following mprotect is not
445 	 * allowed. Note that for r/w the kernel already prevents the mmap. */
446 	p = mmap(NULL,
447 		 mfd_def_size,
448 		 PROT_READ,
449 		 MAP_SHARED,
450 		 fd,
451 		 0);
452 	if (p != MAP_FAILED) {
453 		r = mprotect(p, mfd_def_size, PROT_READ | PROT_WRITE);
454 		if (r >= 0) {
455 			printf("mmap()+mprotect() didn't fail as expected\n");
456 			abort();
457 		}
458 		munmap(p, mfd_def_size);
459 	}
460 
461 	/* verify PUNCH_HOLE fails */
462 	r = fallocate(fd,
463 		      FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
464 		      0,
465 		      mfd_def_size);
466 	if (r >= 0) {
467 		printf("fallocate(PUNCH_HOLE) didn't fail as expected\n");
468 		abort();
469 	}
470 }
471 
mfd_assert_shrink(int fd)472 static void mfd_assert_shrink(int fd)
473 {
474 	int r, fd2;
475 
476 	r = ftruncate(fd, mfd_def_size / 2);
477 	if (r < 0) {
478 		printf("ftruncate(SHRINK) failed: %m\n");
479 		abort();
480 	}
481 
482 	mfd_assert_size(fd, mfd_def_size / 2);
483 
484 	fd2 = mfd_assert_open(fd,
485 			      O_RDWR | O_CREAT | O_TRUNC,
486 			      S_IRUSR | S_IWUSR);
487 	close(fd2);
488 
489 	mfd_assert_size(fd, 0);
490 }
491 
mfd_fail_shrink(int fd)492 static void mfd_fail_shrink(int fd)
493 {
494 	int r;
495 
496 	r = ftruncate(fd, mfd_def_size / 2);
497 	if (r >= 0) {
498 		printf("ftruncate(SHRINK) didn't fail as expected\n");
499 		abort();
500 	}
501 
502 	mfd_fail_open(fd,
503 		      O_RDWR | O_CREAT | O_TRUNC,
504 		      S_IRUSR | S_IWUSR);
505 }
506 
mfd_assert_grow(int fd)507 static void mfd_assert_grow(int fd)
508 {
509 	int r;
510 
511 	r = ftruncate(fd, mfd_def_size * 2);
512 	if (r < 0) {
513 		printf("ftruncate(GROW) failed: %m\n");
514 		abort();
515 	}
516 
517 	mfd_assert_size(fd, mfd_def_size * 2);
518 
519 	r = fallocate(fd,
520 		      0,
521 		      0,
522 		      mfd_def_size * 4);
523 	if (r < 0) {
524 		printf("fallocate(ALLOC) failed: %m\n");
525 		abort();
526 	}
527 
528 	mfd_assert_size(fd, mfd_def_size * 4);
529 }
530 
mfd_fail_grow(int fd)531 static void mfd_fail_grow(int fd)
532 {
533 	int r;
534 
535 	r = ftruncate(fd, mfd_def_size * 2);
536 	if (r >= 0) {
537 		printf("ftruncate(GROW) didn't fail as expected\n");
538 		abort();
539 	}
540 
541 	r = fallocate(fd,
542 		      0,
543 		      0,
544 		      mfd_def_size * 4);
545 	if (r >= 0) {
546 		printf("fallocate(ALLOC) didn't fail as expected\n");
547 		abort();
548 	}
549 }
550 
mfd_assert_grow_write(int fd)551 static void mfd_assert_grow_write(int fd)
552 {
553 	static char *buf;
554 	ssize_t l;
555 
556 	/* hugetlbfs does not support write */
557 	if (hugetlbfs_test)
558 		return;
559 
560 	buf = malloc(mfd_def_size * 8);
561 	if (!buf) {
562 		printf("malloc(%zu) failed: %m\n", mfd_def_size * 8);
563 		abort();
564 	}
565 
566 	l = pwrite(fd, buf, mfd_def_size * 8, 0);
567 	if (l != (mfd_def_size * 8)) {
568 		printf("pwrite() failed: %m\n");
569 		abort();
570 	}
571 
572 	mfd_assert_size(fd, mfd_def_size * 8);
573 }
574 
mfd_fail_grow_write(int fd)575 static void mfd_fail_grow_write(int fd)
576 {
577 	static char *buf;
578 	ssize_t l;
579 
580 	/* hugetlbfs does not support write */
581 	if (hugetlbfs_test)
582 		return;
583 
584 	buf = malloc(mfd_def_size * 8);
585 	if (!buf) {
586 		printf("malloc(%zu) failed: %m\n", mfd_def_size * 8);
587 		abort();
588 	}
589 
590 	l = pwrite(fd, buf, mfd_def_size * 8, 0);
591 	if (l == (mfd_def_size * 8)) {
592 		printf("pwrite() didn't fail as expected\n");
593 		abort();
594 	}
595 }
596 
idle_thread_fn(void * arg)597 static int idle_thread_fn(void *arg)
598 {
599 	sigset_t set;
600 	int sig;
601 
602 	/* dummy waiter; SIGTERM terminates us anyway */
603 	sigemptyset(&set);
604 	sigaddset(&set, SIGTERM);
605 	sigwait(&set, &sig);
606 
607 	return 0;
608 }
609 
spawn_idle_thread(unsigned int flags)610 static pid_t spawn_idle_thread(unsigned int flags)
611 {
612 	uint8_t *stack;
613 	pid_t pid;
614 
615 	stack = malloc(STACK_SIZE);
616 	if (!stack) {
617 		printf("malloc(STACK_SIZE) failed: %m\n");
618 		abort();
619 	}
620 
621 	pid = clone(idle_thread_fn,
622 		    stack + STACK_SIZE,
623 		    SIGCHLD | flags,
624 		    NULL);
625 	if (pid < 0) {
626 		printf("clone() failed: %m\n");
627 		abort();
628 	}
629 
630 	return pid;
631 }
632 
join_idle_thread(pid_t pid)633 static void join_idle_thread(pid_t pid)
634 {
635 	kill(pid, SIGTERM);
636 	waitpid(pid, NULL, 0);
637 }
638 
639 /*
640  * Test memfd_create() syscall
641  * Verify syscall-argument validation, including name checks, flag validation
642  * and more.
643  */
test_create(void)644 static void test_create(void)
645 {
646 	char buf[2048];
647 	int fd;
648 
649 	printf("%s CREATE\n", memfd_str);
650 
651 	/* test NULL name */
652 	mfd_fail_new(NULL, 0);
653 
654 	/* test over-long name (not zero-terminated) */
655 	memset(buf, 0xff, sizeof(buf));
656 	mfd_fail_new(buf, 0);
657 
658 	/* test over-long zero-terminated name */
659 	memset(buf, 0xff, sizeof(buf));
660 	buf[sizeof(buf) - 1] = 0;
661 	mfd_fail_new(buf, 0);
662 
663 	/* verify "" is a valid name */
664 	fd = mfd_assert_new("", 0, 0);
665 	close(fd);
666 
667 	/* verify invalid O_* open flags */
668 	mfd_fail_new("", 0x0100);
669 	mfd_fail_new("", ~MFD_CLOEXEC);
670 	mfd_fail_new("", ~MFD_ALLOW_SEALING);
671 	mfd_fail_new("", ~0);
672 	mfd_fail_new("", 0x80000000U);
673 
674 	/* verify MFD_CLOEXEC is allowed */
675 	fd = mfd_assert_new("", 0, MFD_CLOEXEC);
676 	close(fd);
677 
678 	/* verify MFD_ALLOW_SEALING is allowed */
679 	fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING);
680 	close(fd);
681 
682 	/* verify MFD_ALLOW_SEALING | MFD_CLOEXEC is allowed */
683 	fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING | MFD_CLOEXEC);
684 	close(fd);
685 }
686 
687 /*
688  * Test basic sealing
689  * A very basic sealing test to see whether setting/retrieving seals works.
690  */
test_basic(void)691 static void test_basic(void)
692 {
693 	int fd;
694 
695 	printf("%s BASIC\n", memfd_str);
696 
697 	fd = mfd_assert_new("kern_memfd_basic",
698 			    mfd_def_size,
699 			    MFD_CLOEXEC | MFD_ALLOW_SEALING);
700 
701 	/* add basic seals */
702 	mfd_assert_has_seals(fd, 0);
703 	mfd_assert_add_seals(fd, F_SEAL_SHRINK |
704 				 F_SEAL_WRITE);
705 	mfd_assert_has_seals(fd, F_SEAL_SHRINK |
706 				 F_SEAL_WRITE);
707 
708 	/* add them again */
709 	mfd_assert_add_seals(fd, F_SEAL_SHRINK |
710 				 F_SEAL_WRITE);
711 	mfd_assert_has_seals(fd, F_SEAL_SHRINK |
712 				 F_SEAL_WRITE);
713 
714 	/* add more seals and seal against sealing */
715 	mfd_assert_add_seals(fd, F_SEAL_GROW | F_SEAL_SEAL);
716 	mfd_assert_has_seals(fd, F_SEAL_SHRINK |
717 				 F_SEAL_GROW |
718 				 F_SEAL_WRITE |
719 				 F_SEAL_SEAL);
720 
721 	/* verify that sealing no longer works */
722 	mfd_fail_add_seals(fd, F_SEAL_GROW);
723 	mfd_fail_add_seals(fd, 0);
724 
725 	close(fd);
726 
727 	/* verify sealing does not work without MFD_ALLOW_SEALING */
728 	fd = mfd_assert_new("kern_memfd_basic",
729 			    mfd_def_size,
730 			    MFD_CLOEXEC);
731 	mfd_assert_has_seals(fd, F_SEAL_SEAL);
732 	mfd_fail_add_seals(fd, F_SEAL_SHRINK |
733 			       F_SEAL_GROW |
734 			       F_SEAL_WRITE);
735 	mfd_assert_has_seals(fd, F_SEAL_SEAL);
736 	close(fd);
737 }
738 
739 /*
740  * Test SEAL_WRITE
741  * Test whether SEAL_WRITE actually prevents modifications.
742  */
test_seal_write(void)743 static void test_seal_write(void)
744 {
745 	int fd;
746 
747 	printf("%s SEAL-WRITE\n", memfd_str);
748 
749 	fd = mfd_assert_new("kern_memfd_seal_write",
750 			    mfd_def_size,
751 			    MFD_CLOEXEC | MFD_ALLOW_SEALING);
752 	mfd_assert_has_seals(fd, 0);
753 	mfd_assert_add_seals(fd, F_SEAL_WRITE);
754 	mfd_assert_has_seals(fd, F_SEAL_WRITE);
755 
756 	mfd_assert_read(fd);
757 	mfd_fail_write(fd);
758 	mfd_assert_shrink(fd);
759 	mfd_assert_grow(fd);
760 	mfd_fail_grow_write(fd);
761 
762 	close(fd);
763 }
764 
765 /*
766  * Test SEAL_FUTURE_WRITE
767  * Test whether SEAL_FUTURE_WRITE actually prevents modifications.
768  */
test_seal_future_write(void)769 static void test_seal_future_write(void)
770 {
771 	int fd, fd2;
772 	void *p;
773 
774 	printf("%s SEAL-FUTURE-WRITE\n", memfd_str);
775 
776 	fd = mfd_assert_new("kern_memfd_seal_future_write",
777 			    mfd_def_size,
778 			    MFD_CLOEXEC | MFD_ALLOW_SEALING);
779 
780 	p = mfd_assert_mmap_shared(fd);
781 
782 	mfd_assert_has_seals(fd, 0);
783 
784 	mfd_assert_add_seals(fd, F_SEAL_FUTURE_WRITE);
785 	mfd_assert_has_seals(fd, F_SEAL_FUTURE_WRITE);
786 
787 	/* read should pass, writes should fail */
788 	mfd_assert_read(fd);
789 	mfd_assert_read_shared(fd);
790 	mfd_fail_write(fd);
791 
792 	fd2 = mfd_assert_reopen_fd(fd);
793 	/* read should pass, writes should still fail */
794 	mfd_assert_read(fd2);
795 	mfd_assert_read_shared(fd2);
796 	mfd_fail_write(fd2);
797 
798 	mfd_assert_fork_private_write(fd);
799 
800 	munmap(p, mfd_def_size);
801 	close(fd2);
802 	close(fd);
803 }
804 
805 /*
806  * Test SEAL_SHRINK
807  * Test whether SEAL_SHRINK actually prevents shrinking
808  */
test_seal_shrink(void)809 static void test_seal_shrink(void)
810 {
811 	int fd;
812 
813 	printf("%s SEAL-SHRINK\n", memfd_str);
814 
815 	fd = mfd_assert_new("kern_memfd_seal_shrink",
816 			    mfd_def_size,
817 			    MFD_CLOEXEC | MFD_ALLOW_SEALING);
818 	mfd_assert_has_seals(fd, 0);
819 	mfd_assert_add_seals(fd, F_SEAL_SHRINK);
820 	mfd_assert_has_seals(fd, F_SEAL_SHRINK);
821 
822 	mfd_assert_read(fd);
823 	mfd_assert_write(fd);
824 	mfd_fail_shrink(fd);
825 	mfd_assert_grow(fd);
826 	mfd_assert_grow_write(fd);
827 
828 	close(fd);
829 }
830 
831 /*
832  * Test SEAL_GROW
833  * Test whether SEAL_GROW actually prevents growing
834  */
test_seal_grow(void)835 static void test_seal_grow(void)
836 {
837 	int fd;
838 
839 	printf("%s SEAL-GROW\n", memfd_str);
840 
841 	fd = mfd_assert_new("kern_memfd_seal_grow",
842 			    mfd_def_size,
843 			    MFD_CLOEXEC | MFD_ALLOW_SEALING);
844 	mfd_assert_has_seals(fd, 0);
845 	mfd_assert_add_seals(fd, F_SEAL_GROW);
846 	mfd_assert_has_seals(fd, F_SEAL_GROW);
847 
848 	mfd_assert_read(fd);
849 	mfd_assert_write(fd);
850 	mfd_assert_shrink(fd);
851 	mfd_fail_grow(fd);
852 	mfd_fail_grow_write(fd);
853 
854 	close(fd);
855 }
856 
857 /*
858  * Test SEAL_SHRINK | SEAL_GROW
859  * Test whether SEAL_SHRINK | SEAL_GROW actually prevents resizing
860  */
test_seal_resize(void)861 static void test_seal_resize(void)
862 {
863 	int fd;
864 
865 	printf("%s SEAL-RESIZE\n", memfd_str);
866 
867 	fd = mfd_assert_new("kern_memfd_seal_resize",
868 			    mfd_def_size,
869 			    MFD_CLOEXEC | MFD_ALLOW_SEALING);
870 	mfd_assert_has_seals(fd, 0);
871 	mfd_assert_add_seals(fd, F_SEAL_SHRINK | F_SEAL_GROW);
872 	mfd_assert_has_seals(fd, F_SEAL_SHRINK | F_SEAL_GROW);
873 
874 	mfd_assert_read(fd);
875 	mfd_assert_write(fd);
876 	mfd_fail_shrink(fd);
877 	mfd_fail_grow(fd);
878 	mfd_fail_grow_write(fd);
879 
880 	close(fd);
881 }
882 
883 /*
884  * Test sharing via dup()
885  * Test that seals are shared between dupped FDs and they're all equal.
886  */
test_share_dup(char * banner,char * b_suffix)887 static void test_share_dup(char *banner, char *b_suffix)
888 {
889 	int fd, fd2;
890 
891 	printf("%s %s %s\n", memfd_str, banner, b_suffix);
892 
893 	fd = mfd_assert_new("kern_memfd_share_dup",
894 			    mfd_def_size,
895 			    MFD_CLOEXEC | MFD_ALLOW_SEALING);
896 	mfd_assert_has_seals(fd, 0);
897 
898 	fd2 = mfd_assert_dup(fd);
899 	mfd_assert_has_seals(fd2, 0);
900 
901 	mfd_assert_add_seals(fd, F_SEAL_WRITE);
902 	mfd_assert_has_seals(fd, F_SEAL_WRITE);
903 	mfd_assert_has_seals(fd2, F_SEAL_WRITE);
904 
905 	mfd_assert_add_seals(fd2, F_SEAL_SHRINK);
906 	mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK);
907 	mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK);
908 
909 	mfd_assert_add_seals(fd, F_SEAL_SEAL);
910 	mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL);
911 	mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL);
912 
913 	mfd_fail_add_seals(fd, F_SEAL_GROW);
914 	mfd_fail_add_seals(fd2, F_SEAL_GROW);
915 	mfd_fail_add_seals(fd, F_SEAL_SEAL);
916 	mfd_fail_add_seals(fd2, F_SEAL_SEAL);
917 
918 	close(fd2);
919 
920 	mfd_fail_add_seals(fd, F_SEAL_GROW);
921 	close(fd);
922 }
923 
924 /*
925  * Test sealing with active mmap()s
926  * Modifying seals is only allowed if no other mmap() refs exist.
927  */
test_share_mmap(char * banner,char * b_suffix)928 static void test_share_mmap(char *banner, char *b_suffix)
929 {
930 	int fd;
931 	void *p;
932 
933 	printf("%s %s %s\n", memfd_str,  banner, b_suffix);
934 
935 	fd = mfd_assert_new("kern_memfd_share_mmap",
936 			    mfd_def_size,
937 			    MFD_CLOEXEC | MFD_ALLOW_SEALING);
938 	mfd_assert_has_seals(fd, 0);
939 
940 	/* shared/writable ref prevents sealing WRITE, but allows others */
941 	p = mfd_assert_mmap_shared(fd);
942 	mfd_fail_add_seals(fd, F_SEAL_WRITE);
943 	mfd_assert_has_seals(fd, 0);
944 	mfd_assert_add_seals(fd, F_SEAL_SHRINK);
945 	mfd_assert_has_seals(fd, F_SEAL_SHRINK);
946 	munmap(p, mfd_def_size);
947 
948 	/* readable ref allows sealing */
949 	p = mfd_assert_mmap_private(fd);
950 	mfd_assert_add_seals(fd, F_SEAL_WRITE);
951 	mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK);
952 	munmap(p, mfd_def_size);
953 
954 	close(fd);
955 }
956 
957 /*
958  * Test sealing with open(/proc/self/fd/%d)
959  * Via /proc we can get access to a separate file-context for the same memfd.
960  * This is *not* like dup(), but like a real separate open(). Make sure the
961  * semantics are as expected and we correctly check for RDONLY / WRONLY / RDWR.
962  */
test_share_open(char * banner,char * b_suffix)963 static void test_share_open(char *banner, char *b_suffix)
964 {
965 	int fd, fd2;
966 
967 	printf("%s %s %s\n", memfd_str, banner, b_suffix);
968 
969 	fd = mfd_assert_new("kern_memfd_share_open",
970 			    mfd_def_size,
971 			    MFD_CLOEXEC | MFD_ALLOW_SEALING);
972 	mfd_assert_has_seals(fd, 0);
973 
974 	fd2 = mfd_assert_open(fd, O_RDWR, 0);
975 	mfd_assert_add_seals(fd, F_SEAL_WRITE);
976 	mfd_assert_has_seals(fd, F_SEAL_WRITE);
977 	mfd_assert_has_seals(fd2, F_SEAL_WRITE);
978 
979 	mfd_assert_add_seals(fd2, F_SEAL_SHRINK);
980 	mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK);
981 	mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK);
982 
983 	close(fd);
984 	fd = mfd_assert_open(fd2, O_RDONLY, 0);
985 
986 	mfd_fail_add_seals(fd, F_SEAL_SEAL);
987 	mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK);
988 	mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK);
989 
990 	close(fd2);
991 	fd2 = mfd_assert_open(fd, O_RDWR, 0);
992 
993 	mfd_assert_add_seals(fd2, F_SEAL_SEAL);
994 	mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL);
995 	mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL);
996 
997 	close(fd2);
998 	close(fd);
999 }
1000 
1001 /*
1002  * Test sharing via fork()
1003  * Test whether seal-modifications work as expected with forked childs.
1004  */
test_share_fork(char * banner,char * b_suffix)1005 static void test_share_fork(char *banner, char *b_suffix)
1006 {
1007 	int fd;
1008 	pid_t pid;
1009 
1010 	printf("%s %s %s\n", memfd_str, banner, b_suffix);
1011 
1012 	fd = mfd_assert_new("kern_memfd_share_fork",
1013 			    mfd_def_size,
1014 			    MFD_CLOEXEC | MFD_ALLOW_SEALING);
1015 	mfd_assert_has_seals(fd, 0);
1016 
1017 	pid = spawn_idle_thread(0);
1018 	mfd_assert_add_seals(fd, F_SEAL_SEAL);
1019 	mfd_assert_has_seals(fd, F_SEAL_SEAL);
1020 
1021 	mfd_fail_add_seals(fd, F_SEAL_WRITE);
1022 	mfd_assert_has_seals(fd, F_SEAL_SEAL);
1023 
1024 	join_idle_thread(pid);
1025 
1026 	mfd_fail_add_seals(fd, F_SEAL_WRITE);
1027 	mfd_assert_has_seals(fd, F_SEAL_SEAL);
1028 
1029 	close(fd);
1030 }
1031 
main(int argc,char ** argv)1032 int main(int argc, char **argv)
1033 {
1034 	pid_t pid;
1035 
1036 	if (argc == 2) {
1037 		if (!strcmp(argv[1], "hugetlbfs")) {
1038 			unsigned long hpage_size = default_huge_page_size();
1039 
1040 			if (!hpage_size) {
1041 				printf("Unable to determine huge page size\n");
1042 				abort();
1043 			}
1044 
1045 			hugetlbfs_test = 1;
1046 			memfd_str = MEMFD_HUGE_STR;
1047 			mfd_def_size = hpage_size * 2;
1048 		} else {
1049 			printf("Unknown option: %s\n", argv[1]);
1050 			abort();
1051 		}
1052 	}
1053 
1054 	test_create();
1055 	test_basic();
1056 
1057 	test_seal_write();
1058 	test_seal_future_write();
1059 	test_seal_shrink();
1060 	test_seal_grow();
1061 	test_seal_resize();
1062 
1063 	test_share_dup("SHARE-DUP", "");
1064 	test_share_mmap("SHARE-MMAP", "");
1065 	test_share_open("SHARE-OPEN", "");
1066 	test_share_fork("SHARE-FORK", "");
1067 
1068 	/* Run test-suite in a multi-threaded environment with a shared
1069 	 * file-table. */
1070 	pid = spawn_idle_thread(CLONE_FILES | CLONE_FS | CLONE_VM);
1071 	test_share_dup("SHARE-DUP", SHARED_FT_STR);
1072 	test_share_mmap("SHARE-MMAP", SHARED_FT_STR);
1073 	test_share_open("SHARE-OPEN", SHARED_FT_STR);
1074 	test_share_fork("SHARE-FORK", SHARED_FT_STR);
1075 	join_idle_thread(pid);
1076 
1077 	printf("memfd: DONE\n");
1078 
1079 	return 0;
1080 }
1081