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 */
34static size_t mfd_def_size = MFD_DEF_SIZE;
35static const char *memfd_str = MEMFD_STR;
36
37static 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
57static 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
73static 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
86static 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
99static 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
110static 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
123static 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
142static 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
158static 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
171static 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
189static 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
207static 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
222static 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
235static 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. */
275static 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
293static 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
327static 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
407static 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
472static 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
492static 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
507static 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
531static 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
551static 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
575static 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
597static 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
610static 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
633static 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 */
644static 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 */
691static 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 */
743static 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 */
769static 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 */
809static 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 */
835static 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 */
861static 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 */
887static 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 */
928static 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 */
963static 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 */
1005static 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
1032int 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