1615 lines · c
1// SPDX-License-Identifier: GPL-2.02#define _GNU_SOURCE3#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#include <ctype.h>22 23#include "common.h"24 25#define MEMFD_STR "memfd:"26#define MEMFD_HUGE_STR "memfd-hugetlb:"27#define SHARED_FT_STR "(shared file-table)"28 29#define MFD_DEF_SIZE 819230#define STACK_SIZE 6553631 32#define F_SEAL_EXEC 0x002033 34#define F_WX_SEALS (F_SEAL_SHRINK | \35 F_SEAL_GROW | \36 F_SEAL_WRITE | \37 F_SEAL_FUTURE_WRITE | \38 F_SEAL_EXEC)39 40#define MFD_NOEXEC_SEAL 0x0008U41 42/*43 * Default is not to test hugetlbfs44 */45static size_t mfd_def_size = MFD_DEF_SIZE;46static const char *memfd_str = MEMFD_STR;47 48static ssize_t fd2name(int fd, char *buf, size_t bufsize)49{50 char buf1[PATH_MAX];51 int size;52 ssize_t nbytes;53 54 size = snprintf(buf1, PATH_MAX, "/proc/self/fd/%d", fd);55 if (size < 0) {56 printf("snprintf(%d) failed on %m\n", fd);57 abort();58 }59 60 /*61 * reserver one byte for string termination.62 */63 nbytes = readlink(buf1, buf, bufsize-1);64 if (nbytes == -1) {65 printf("readlink(%s) failed %m\n", buf1);66 abort();67 }68 buf[nbytes] = '\0';69 return nbytes;70}71 72static int mfd_assert_new(const char *name, loff_t sz, unsigned int flags)73{74 int r, fd;75 76 fd = sys_memfd_create(name, flags);77 if (fd < 0) {78 printf("memfd_create(\"%s\", %u) failed: %m\n",79 name, flags);80 abort();81 }82 83 r = ftruncate(fd, sz);84 if (r < 0) {85 printf("ftruncate(%llu) failed: %m\n", (unsigned long long)sz);86 abort();87 }88 89 return fd;90}91 92static void sysctl_assert_write(const char *val)93{94 int fd = open("/proc/sys/vm/memfd_noexec", O_WRONLY | O_CLOEXEC);95 96 if (fd < 0) {97 printf("open sysctl failed: %m\n");98 abort();99 }100 101 if (write(fd, val, strlen(val)) < 0) {102 printf("write sysctl %s failed: %m\n", val);103 abort();104 }105}106 107static void sysctl_fail_write(const char *val)108{109 int fd = open("/proc/sys/vm/memfd_noexec", O_WRONLY | O_CLOEXEC);110 111 if (fd < 0) {112 printf("open sysctl failed: %m\n");113 abort();114 }115 116 if (write(fd, val, strlen(val)) >= 0) {117 printf("write sysctl %s succeeded, but failure expected\n",118 val);119 abort();120 }121}122 123static void sysctl_assert_equal(const char *val)124{125 char *p, buf[128] = {};126 int fd = open("/proc/sys/vm/memfd_noexec", O_RDONLY | O_CLOEXEC);127 128 if (fd < 0) {129 printf("open sysctl failed: %m\n");130 abort();131 }132 133 if (read(fd, buf, sizeof(buf)) < 0) {134 printf("read sysctl failed: %m\n");135 abort();136 }137 138 /* Strip trailing whitespace. */139 p = buf;140 while (!isspace(*p))141 p++;142 *p = '\0';143 144 if (strcmp(buf, val) != 0) {145 printf("unexpected sysctl value: expected %s, got %s\n", val, buf);146 abort();147 }148}149 150static int mfd_assert_reopen_fd(int fd_in)151{152 int fd;153 char path[100];154 155 sprintf(path, "/proc/self/fd/%d", fd_in);156 157 fd = open(path, O_RDWR);158 if (fd < 0) {159 printf("re-open of existing fd %d failed\n", fd_in);160 abort();161 }162 163 return fd;164}165 166static void mfd_fail_new(const char *name, unsigned int flags)167{168 int r;169 170 r = sys_memfd_create(name, flags);171 if (r >= 0) {172 printf("memfd_create(\"%s\", %u) succeeded, but failure expected\n",173 name, flags);174 close(r);175 abort();176 }177}178 179static unsigned int mfd_assert_get_seals(int fd)180{181 int r;182 183 r = fcntl(fd, F_GET_SEALS);184 if (r < 0) {185 printf("GET_SEALS(%d) failed: %m\n", fd);186 abort();187 }188 189 return (unsigned int)r;190}191 192static void mfd_assert_has_seals(int fd, unsigned int seals)193{194 char buf[PATH_MAX];195 unsigned int s;196 fd2name(fd, buf, PATH_MAX);197 198 s = mfd_assert_get_seals(fd);199 if (s != seals) {200 printf("%u != %u = GET_SEALS(%s)\n", seals, s, buf);201 abort();202 }203}204 205static void mfd_assert_add_seals(int fd, unsigned int seals)206{207 int r;208 unsigned int s;209 210 s = mfd_assert_get_seals(fd);211 r = fcntl(fd, F_ADD_SEALS, seals);212 if (r < 0) {213 printf("ADD_SEALS(%d, %u -> %u) failed: %m\n", fd, s, seals);214 abort();215 }216}217 218static void mfd_fail_add_seals(int fd, unsigned int seals)219{220 int r;221 unsigned int s;222 223 r = fcntl(fd, F_GET_SEALS);224 if (r < 0)225 s = 0;226 else227 s = (unsigned int)r;228 229 r = fcntl(fd, F_ADD_SEALS, seals);230 if (r >= 0) {231 printf("ADD_SEALS(%d, %u -> %u) didn't fail as expected\n",232 fd, s, seals);233 abort();234 }235}236 237static void mfd_assert_size(int fd, size_t size)238{239 struct stat st;240 int r;241 242 r = fstat(fd, &st);243 if (r < 0) {244 printf("fstat(%d) failed: %m\n", fd);245 abort();246 } else if (st.st_size != size) {247 printf("wrong file size %lld, but expected %lld\n",248 (long long)st.st_size, (long long)size);249 abort();250 }251}252 253static int mfd_assert_dup(int fd)254{255 int r;256 257 r = dup(fd);258 if (r < 0) {259 printf("dup(%d) failed: %m\n", fd);260 abort();261 }262 263 return r;264}265 266static void *mfd_assert_mmap_shared(int fd)267{268 void *p;269 270 p = mmap(NULL,271 mfd_def_size,272 PROT_READ | PROT_WRITE,273 MAP_SHARED,274 fd,275 0);276 if (p == MAP_FAILED) {277 printf("mmap() failed: %m\n");278 abort();279 }280 281 return p;282}283 284static void *mfd_assert_mmap_private(int fd)285{286 void *p;287 288 p = mmap(NULL,289 mfd_def_size,290 PROT_READ,291 MAP_PRIVATE,292 fd,293 0);294 if (p == MAP_FAILED) {295 printf("mmap() failed: %m\n");296 abort();297 }298 299 return p;300}301 302static int mfd_assert_open(int fd, int flags, mode_t mode)303{304 char buf[512];305 int r;306 307 sprintf(buf, "/proc/self/fd/%d", fd);308 r = open(buf, flags, mode);309 if (r < 0) {310 printf("open(%s) failed: %m\n", buf);311 abort();312 }313 314 return r;315}316 317static void mfd_fail_open(int fd, int flags, mode_t mode)318{319 char buf[512];320 int r;321 322 sprintf(buf, "/proc/self/fd/%d", fd);323 r = open(buf, flags, mode);324 if (r >= 0) {325 printf("open(%s) didn't fail as expected\n", buf);326 abort();327 }328}329 330static void mfd_assert_read(int fd)331{332 char buf[16];333 void *p;334 ssize_t l;335 336 l = read(fd, buf, sizeof(buf));337 if (l != sizeof(buf)) {338 printf("read() failed: %m\n");339 abort();340 }341 342 /* verify PROT_READ *is* allowed */343 p = mmap(NULL,344 mfd_def_size,345 PROT_READ,346 MAP_PRIVATE,347 fd,348 0);349 if (p == MAP_FAILED) {350 printf("mmap() failed: %m\n");351 abort();352 }353 munmap(p, mfd_def_size);354 355 /* verify MAP_PRIVATE is *always* allowed (even writable) */356 p = mmap(NULL,357 mfd_def_size,358 PROT_READ | PROT_WRITE,359 MAP_PRIVATE,360 fd,361 0);362 if (p == MAP_FAILED) {363 printf("mmap() failed: %m\n");364 abort();365 }366 munmap(p, mfd_def_size);367}368 369/* Test that PROT_READ + MAP_SHARED mappings work. */370static void mfd_assert_read_shared(int fd)371{372 void *p;373 374 /* verify PROT_READ and MAP_SHARED *is* allowed */375 p = mmap(NULL,376 mfd_def_size,377 PROT_READ,378 MAP_SHARED,379 fd,380 0);381 if (p == MAP_FAILED) {382 printf("mmap() failed: %m\n");383 abort();384 }385 munmap(p, mfd_def_size);386}387 388static void mfd_assert_fork_private_write(int fd)389{390 int *p;391 pid_t pid;392 393 p = mmap(NULL,394 mfd_def_size,395 PROT_READ | PROT_WRITE,396 MAP_PRIVATE,397 fd,398 0);399 if (p == MAP_FAILED) {400 printf("mmap() failed: %m\n");401 abort();402 }403 404 p[0] = 22;405 406 pid = fork();407 if (pid == 0) {408 p[0] = 33;409 exit(0);410 } else {411 waitpid(pid, NULL, 0);412 413 if (p[0] != 22) {414 printf("MAP_PRIVATE copy-on-write failed: %m\n");415 abort();416 }417 }418 419 munmap(p, mfd_def_size);420}421 422static void mfd_assert_write(int fd)423{424 ssize_t l;425 void *p;426 int r;427 428 /*429 * huegtlbfs does not support write, but we want to430 * verify everything else here.431 */432 if (!hugetlbfs_test) {433 /* verify write() succeeds */434 l = write(fd, "\0\0\0\0", 4);435 if (l != 4) {436 printf("write() failed: %m\n");437 abort();438 }439 }440 441 /* verify PROT_READ | PROT_WRITE is allowed */442 p = mmap(NULL,443 mfd_def_size,444 PROT_READ | PROT_WRITE,445 MAP_SHARED,446 fd,447 0);448 if (p == MAP_FAILED) {449 printf("mmap() failed: %m\n");450 abort();451 }452 *(char *)p = 0;453 munmap(p, mfd_def_size);454 455 /* verify PROT_WRITE is allowed */456 p = mmap(NULL,457 mfd_def_size,458 PROT_WRITE,459 MAP_SHARED,460 fd,461 0);462 if (p == MAP_FAILED) {463 printf("mmap() failed: %m\n");464 abort();465 }466 *(char *)p = 0;467 munmap(p, mfd_def_size);468 469 /* verify PROT_READ with MAP_SHARED is allowed and a following470 * mprotect(PROT_WRITE) allows writing */471 p = mmap(NULL,472 mfd_def_size,473 PROT_READ,474 MAP_SHARED,475 fd,476 0);477 if (p == MAP_FAILED) {478 printf("mmap() failed: %m\n");479 abort();480 }481 482 r = mprotect(p, mfd_def_size, PROT_READ | PROT_WRITE);483 if (r < 0) {484 printf("mprotect() failed: %m\n");485 abort();486 }487 488 *(char *)p = 0;489 munmap(p, mfd_def_size);490 491 /* verify PUNCH_HOLE works */492 r = fallocate(fd,493 FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,494 0,495 mfd_def_size);496 if (r < 0) {497 printf("fallocate(PUNCH_HOLE) failed: %m\n");498 abort();499 }500}501 502static void mfd_fail_write(int fd)503{504 ssize_t l;505 void *p;506 int r;507 508 /* verify write() fails */509 l = write(fd, "data", 4);510 if (l != -EPERM) {511 printf("expected EPERM on write(), but got %d: %m\n", (int)l);512 abort();513 }514 515 /* verify PROT_READ | PROT_WRITE is not allowed */516 p = mmap(NULL,517 mfd_def_size,518 PROT_READ | PROT_WRITE,519 MAP_SHARED,520 fd,521 0);522 if (p != MAP_FAILED) {523 printf("mmap() didn't fail as expected\n");524 abort();525 }526 527 /* verify PROT_WRITE is not allowed */528 p = mmap(NULL,529 mfd_def_size,530 PROT_WRITE,531 MAP_SHARED,532 fd,533 0);534 if (p != MAP_FAILED) {535 printf("mmap() didn't fail as expected\n");536 abort();537 }538 539 /* Verify PROT_READ with MAP_SHARED with a following mprotect is not540 * allowed. Note that for r/w the kernel already prevents the mmap. */541 p = mmap(NULL,542 mfd_def_size,543 PROT_READ,544 MAP_SHARED,545 fd,546 0);547 if (p != MAP_FAILED) {548 r = mprotect(p, mfd_def_size, PROT_READ | PROT_WRITE);549 if (r >= 0) {550 printf("mmap()+mprotect() didn't fail as expected\n");551 abort();552 }553 munmap(p, mfd_def_size);554 }555 556 /* verify PUNCH_HOLE fails */557 r = fallocate(fd,558 FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,559 0,560 mfd_def_size);561 if (r >= 0) {562 printf("fallocate(PUNCH_HOLE) didn't fail as expected\n");563 abort();564 }565}566 567static void mfd_assert_shrink(int fd)568{569 int r, fd2;570 571 r = ftruncate(fd, mfd_def_size / 2);572 if (r < 0) {573 printf("ftruncate(SHRINK) failed: %m\n");574 abort();575 }576 577 mfd_assert_size(fd, mfd_def_size / 2);578 579 fd2 = mfd_assert_open(fd,580 O_RDWR | O_CREAT | O_TRUNC,581 S_IRUSR | S_IWUSR);582 close(fd2);583 584 mfd_assert_size(fd, 0);585}586 587static void mfd_fail_shrink(int fd)588{589 int r;590 591 r = ftruncate(fd, mfd_def_size / 2);592 if (r >= 0) {593 printf("ftruncate(SHRINK) didn't fail as expected\n");594 abort();595 }596 597 mfd_fail_open(fd,598 O_RDWR | O_CREAT | O_TRUNC,599 S_IRUSR | S_IWUSR);600}601 602static void mfd_assert_grow(int fd)603{604 int r;605 606 r = ftruncate(fd, mfd_def_size * 2);607 if (r < 0) {608 printf("ftruncate(GROW) failed: %m\n");609 abort();610 }611 612 mfd_assert_size(fd, mfd_def_size * 2);613 614 r = fallocate(fd,615 0,616 0,617 mfd_def_size * 4);618 if (r < 0) {619 printf("fallocate(ALLOC) failed: %m\n");620 abort();621 }622 623 mfd_assert_size(fd, mfd_def_size * 4);624}625 626static void mfd_fail_grow(int fd)627{628 int r;629 630 r = ftruncate(fd, mfd_def_size * 2);631 if (r >= 0) {632 printf("ftruncate(GROW) didn't fail as expected\n");633 abort();634 }635 636 r = fallocate(fd,637 0,638 0,639 mfd_def_size * 4);640 if (r >= 0) {641 printf("fallocate(ALLOC) didn't fail as expected\n");642 abort();643 }644}645 646static void mfd_assert_grow_write(int fd)647{648 static char *buf;649 ssize_t l;650 651 /* hugetlbfs does not support write */652 if (hugetlbfs_test)653 return;654 655 buf = malloc(mfd_def_size * 8);656 if (!buf) {657 printf("malloc(%zu) failed: %m\n", mfd_def_size * 8);658 abort();659 }660 661 l = pwrite(fd, buf, mfd_def_size * 8, 0);662 if (l != (mfd_def_size * 8)) {663 printf("pwrite() failed: %m\n");664 abort();665 }666 667 mfd_assert_size(fd, mfd_def_size * 8);668}669 670static void mfd_fail_grow_write(int fd)671{672 static char *buf;673 ssize_t l;674 675 /* hugetlbfs does not support write */676 if (hugetlbfs_test)677 return;678 679 buf = malloc(mfd_def_size * 8);680 if (!buf) {681 printf("malloc(%zu) failed: %m\n", mfd_def_size * 8);682 abort();683 }684 685 l = pwrite(fd, buf, mfd_def_size * 8, 0);686 if (l == (mfd_def_size * 8)) {687 printf("pwrite() didn't fail as expected\n");688 abort();689 }690}691 692static void mfd_assert_mode(int fd, int mode)693{694 struct stat st;695 char buf[PATH_MAX];696 697 fd2name(fd, buf, PATH_MAX);698 699 if (fstat(fd, &st) < 0) {700 printf("fstat(%s) failed: %m\n", buf);701 abort();702 }703 704 if ((st.st_mode & 07777) != mode) {705 printf("fstat(%s) wrong file mode 0%04o, but expected 0%04o\n",706 buf, (int)st.st_mode & 07777, mode);707 abort();708 }709}710 711static void mfd_assert_chmod(int fd, int mode)712{713 char buf[PATH_MAX];714 715 fd2name(fd, buf, PATH_MAX);716 717 if (fchmod(fd, mode) < 0) {718 printf("fchmod(%s, 0%04o) failed: %m\n", buf, mode);719 abort();720 }721 722 mfd_assert_mode(fd, mode);723}724 725static void mfd_fail_chmod(int fd, int mode)726{727 struct stat st;728 char buf[PATH_MAX];729 730 fd2name(fd, buf, PATH_MAX);731 732 if (fstat(fd, &st) < 0) {733 printf("fstat(%s) failed: %m\n", buf);734 abort();735 }736 737 if (fchmod(fd, mode) == 0) {738 printf("fchmod(%s, 0%04o) didn't fail as expected\n",739 buf, mode);740 abort();741 }742 743 /* verify that file mode bits did not change */744 mfd_assert_mode(fd, st.st_mode & 07777);745}746 747static int idle_thread_fn(void *arg)748{749 sigset_t set;750 int sig;751 752 /* dummy waiter; SIGTERM terminates us anyway */753 sigemptyset(&set);754 sigaddset(&set, SIGTERM);755 sigwait(&set, &sig);756 757 return 0;758}759 760static pid_t spawn_thread(unsigned int flags, int (*fn)(void *), void *arg)761{762 uint8_t *stack;763 pid_t pid;764 765 stack = malloc(STACK_SIZE);766 if (!stack) {767 printf("malloc(STACK_SIZE) failed: %m\n");768 abort();769 }770 771 pid = clone(fn, stack + STACK_SIZE, SIGCHLD | flags, arg);772 if (pid < 0) {773 printf("clone() failed: %m\n");774 abort();775 }776 777 return pid;778}779 780static void join_thread(pid_t pid)781{782 int wstatus;783 784 if (waitpid(pid, &wstatus, 0) < 0) {785 printf("newpid thread: waitpid() failed: %m\n");786 abort();787 }788 789 if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0) {790 printf("newpid thread: exited with non-zero error code %d\n",791 WEXITSTATUS(wstatus));792 abort();793 }794 795 if (WIFSIGNALED(wstatus)) {796 printf("newpid thread: killed by signal %d\n",797 WTERMSIG(wstatus));798 abort();799 }800}801 802static pid_t spawn_idle_thread(unsigned int flags)803{804 return spawn_thread(flags, idle_thread_fn, NULL);805}806 807static void join_idle_thread(pid_t pid)808{809 kill(pid, SIGTERM);810 waitpid(pid, NULL, 0);811}812 813/*814 * Test memfd_create() syscall815 * Verify syscall-argument validation, including name checks, flag validation816 * and more.817 */818static void test_create(void)819{820 char buf[2048];821 int fd;822 823 printf("%s CREATE\n", memfd_str);824 825 /* test NULL name */826 mfd_fail_new(NULL, 0);827 828 /* test over-long name (not zero-terminated) */829 memset(buf, 0xff, sizeof(buf));830 mfd_fail_new(buf, 0);831 832 /* test over-long zero-terminated name */833 memset(buf, 0xff, sizeof(buf));834 buf[sizeof(buf) - 1] = 0;835 mfd_fail_new(buf, 0);836 837 /* verify "" is a valid name */838 fd = mfd_assert_new("", 0, 0);839 close(fd);840 841 /* verify invalid O_* open flags */842 mfd_fail_new("", 0x0100);843 mfd_fail_new("", ~MFD_CLOEXEC);844 mfd_fail_new("", ~MFD_ALLOW_SEALING);845 mfd_fail_new("", ~0);846 mfd_fail_new("", 0x80000000U);847 848 /* verify EXEC and NOEXEC_SEAL can't both be set */849 mfd_fail_new("", MFD_EXEC | MFD_NOEXEC_SEAL);850 851 /* verify MFD_CLOEXEC is allowed */852 fd = mfd_assert_new("", 0, MFD_CLOEXEC);853 close(fd);854 855 /* verify MFD_ALLOW_SEALING is allowed */856 fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING);857 close(fd);858 859 /* verify MFD_ALLOW_SEALING | MFD_CLOEXEC is allowed */860 fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING | MFD_CLOEXEC);861 close(fd);862}863 864/*865 * Test basic sealing866 * A very basic sealing test to see whether setting/retrieving seals works.867 */868static void test_basic(void)869{870 int fd;871 872 printf("%s BASIC\n", memfd_str);873 874 fd = mfd_assert_new("kern_memfd_basic",875 mfd_def_size,876 MFD_CLOEXEC | MFD_ALLOW_SEALING);877 878 /* add basic seals */879 mfd_assert_has_seals(fd, 0);880 mfd_assert_add_seals(fd, F_SEAL_SHRINK |881 F_SEAL_WRITE);882 mfd_assert_has_seals(fd, F_SEAL_SHRINK |883 F_SEAL_WRITE);884 885 /* add them again */886 mfd_assert_add_seals(fd, F_SEAL_SHRINK |887 F_SEAL_WRITE);888 mfd_assert_has_seals(fd, F_SEAL_SHRINK |889 F_SEAL_WRITE);890 891 /* add more seals and seal against sealing */892 mfd_assert_add_seals(fd, F_SEAL_GROW | F_SEAL_SEAL);893 mfd_assert_has_seals(fd, F_SEAL_SHRINK |894 F_SEAL_GROW |895 F_SEAL_WRITE |896 F_SEAL_SEAL);897 898 /* verify that sealing no longer works */899 mfd_fail_add_seals(fd, F_SEAL_GROW);900 mfd_fail_add_seals(fd, 0);901 902 close(fd);903 904 /* verify sealing does not work without MFD_ALLOW_SEALING */905 fd = mfd_assert_new("kern_memfd_basic",906 mfd_def_size,907 MFD_CLOEXEC);908 mfd_assert_has_seals(fd, F_SEAL_SEAL);909 mfd_fail_add_seals(fd, F_SEAL_SHRINK |910 F_SEAL_GROW |911 F_SEAL_WRITE);912 mfd_assert_has_seals(fd, F_SEAL_SEAL);913 close(fd);914}915 916/*917 * Test SEAL_WRITE918 * Test whether SEAL_WRITE actually prevents modifications.919 */920static void test_seal_write(void)921{922 int fd;923 924 printf("%s SEAL-WRITE\n", memfd_str);925 926 fd = mfd_assert_new("kern_memfd_seal_write",927 mfd_def_size,928 MFD_CLOEXEC | MFD_ALLOW_SEALING);929 mfd_assert_has_seals(fd, 0);930 mfd_assert_add_seals(fd, F_SEAL_WRITE);931 mfd_assert_has_seals(fd, F_SEAL_WRITE);932 933 mfd_assert_read(fd);934 mfd_fail_write(fd);935 mfd_assert_shrink(fd);936 mfd_assert_grow(fd);937 mfd_fail_grow_write(fd);938 939 close(fd);940}941 942/*943 * Test SEAL_FUTURE_WRITE944 * Test whether SEAL_FUTURE_WRITE actually prevents modifications.945 */946static void test_seal_future_write(void)947{948 int fd, fd2;949 void *p;950 951 printf("%s SEAL-FUTURE-WRITE\n", memfd_str);952 953 fd = mfd_assert_new("kern_memfd_seal_future_write",954 mfd_def_size,955 MFD_CLOEXEC | MFD_ALLOW_SEALING);956 957 p = mfd_assert_mmap_shared(fd);958 959 mfd_assert_has_seals(fd, 0);960 961 mfd_assert_add_seals(fd, F_SEAL_FUTURE_WRITE);962 mfd_assert_has_seals(fd, F_SEAL_FUTURE_WRITE);963 964 /* read should pass, writes should fail */965 mfd_assert_read(fd);966 mfd_assert_read_shared(fd);967 mfd_fail_write(fd);968 969 fd2 = mfd_assert_reopen_fd(fd);970 /* read should pass, writes should still fail */971 mfd_assert_read(fd2);972 mfd_assert_read_shared(fd2);973 mfd_fail_write(fd2);974 975 mfd_assert_fork_private_write(fd);976 977 munmap(p, mfd_def_size);978 close(fd2);979 close(fd);980}981 982/*983 * Test SEAL_SHRINK984 * Test whether SEAL_SHRINK actually prevents shrinking985 */986static void test_seal_shrink(void)987{988 int fd;989 990 printf("%s SEAL-SHRINK\n", memfd_str);991 992 fd = mfd_assert_new("kern_memfd_seal_shrink",993 mfd_def_size,994 MFD_CLOEXEC | MFD_ALLOW_SEALING);995 mfd_assert_has_seals(fd, 0);996 mfd_assert_add_seals(fd, F_SEAL_SHRINK);997 mfd_assert_has_seals(fd, F_SEAL_SHRINK);998 999 mfd_assert_read(fd);1000 mfd_assert_write(fd);1001 mfd_fail_shrink(fd);1002 mfd_assert_grow(fd);1003 mfd_assert_grow_write(fd);1004 1005 close(fd);1006}1007 1008/*1009 * Test SEAL_GROW1010 * Test whether SEAL_GROW actually prevents growing1011 */1012static void test_seal_grow(void)1013{1014 int fd;1015 1016 printf("%s SEAL-GROW\n", memfd_str);1017 1018 fd = mfd_assert_new("kern_memfd_seal_grow",1019 mfd_def_size,1020 MFD_CLOEXEC | MFD_ALLOW_SEALING);1021 mfd_assert_has_seals(fd, 0);1022 mfd_assert_add_seals(fd, F_SEAL_GROW);1023 mfd_assert_has_seals(fd, F_SEAL_GROW);1024 1025 mfd_assert_read(fd);1026 mfd_assert_write(fd);1027 mfd_assert_shrink(fd);1028 mfd_fail_grow(fd);1029 mfd_fail_grow_write(fd);1030 1031 close(fd);1032}1033 1034/*1035 * Test SEAL_SHRINK | SEAL_GROW1036 * Test whether SEAL_SHRINK | SEAL_GROW actually prevents resizing1037 */1038static void test_seal_resize(void)1039{1040 int fd;1041 1042 printf("%s SEAL-RESIZE\n", memfd_str);1043 1044 fd = mfd_assert_new("kern_memfd_seal_resize",1045 mfd_def_size,1046 MFD_CLOEXEC | MFD_ALLOW_SEALING);1047 mfd_assert_has_seals(fd, 0);1048 mfd_assert_add_seals(fd, F_SEAL_SHRINK | F_SEAL_GROW);1049 mfd_assert_has_seals(fd, F_SEAL_SHRINK | F_SEAL_GROW);1050 1051 mfd_assert_read(fd);1052 mfd_assert_write(fd);1053 mfd_fail_shrink(fd);1054 mfd_fail_grow(fd);1055 mfd_fail_grow_write(fd);1056 1057 close(fd);1058}1059 1060/*1061 * Test SEAL_EXEC1062 * Test fd is created with exec and allow sealing.1063 * chmod() cannot change x bits after sealing.1064 */1065static void test_exec_seal(void)1066{1067 int fd;1068 1069 printf("%s SEAL-EXEC\n", memfd_str);1070 1071 printf("%s Apply SEAL_EXEC\n", memfd_str);1072 fd = mfd_assert_new("kern_memfd_seal_exec",1073 mfd_def_size,1074 MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_EXEC);1075 1076 mfd_assert_mode(fd, 0777);1077 mfd_assert_chmod(fd, 0644);1078 1079 mfd_assert_has_seals(fd, 0);1080 mfd_assert_add_seals(fd, F_SEAL_EXEC);1081 mfd_assert_has_seals(fd, F_SEAL_EXEC);1082 1083 mfd_assert_chmod(fd, 0600);1084 mfd_fail_chmod(fd, 0777);1085 mfd_fail_chmod(fd, 0670);1086 mfd_fail_chmod(fd, 0605);1087 mfd_fail_chmod(fd, 0700);1088 mfd_fail_chmod(fd, 0100);1089 mfd_assert_chmod(fd, 0666);1090 mfd_assert_write(fd);1091 close(fd);1092 1093 printf("%s Apply ALL_SEALS\n", memfd_str);1094 fd = mfd_assert_new("kern_memfd_seal_exec",1095 mfd_def_size,1096 MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_EXEC);1097 1098 mfd_assert_mode(fd, 0777);1099 mfd_assert_chmod(fd, 0700);1100 1101 mfd_assert_has_seals(fd, 0);1102 mfd_assert_add_seals(fd, F_SEAL_EXEC);1103 mfd_assert_has_seals(fd, F_WX_SEALS);1104 1105 mfd_fail_chmod(fd, 0711);1106 mfd_fail_chmod(fd, 0600);1107 mfd_fail_write(fd);1108 close(fd);1109}1110 1111/*1112 * Test EXEC_NO_SEAL1113 * Test fd is created with exec and not allow sealing.1114 */1115static void test_exec_no_seal(void)1116{1117 int fd;1118 1119 printf("%s EXEC_NO_SEAL\n", memfd_str);1120 1121 /* Create with EXEC but without ALLOW_SEALING */1122 fd = mfd_assert_new("kern_memfd_exec_no_sealing",1123 mfd_def_size,1124 MFD_CLOEXEC | MFD_EXEC);1125 mfd_assert_mode(fd, 0777);1126 mfd_assert_has_seals(fd, F_SEAL_SEAL);1127 mfd_assert_chmod(fd, 0666);1128 close(fd);1129}1130 1131/*1132 * Test memfd_create with MFD_NOEXEC flag1133 */1134static void test_noexec_seal(void)1135{1136 int fd;1137 1138 printf("%s NOEXEC_SEAL\n", memfd_str);1139 1140 /* Create with NOEXEC and ALLOW_SEALING */1141 fd = mfd_assert_new("kern_memfd_noexec",1142 mfd_def_size,1143 MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_NOEXEC_SEAL);1144 mfd_assert_mode(fd, 0666);1145 mfd_assert_has_seals(fd, F_SEAL_EXEC);1146 mfd_fail_chmod(fd, 0777);1147 close(fd);1148 1149 /* Create with NOEXEC but without ALLOW_SEALING */1150 fd = mfd_assert_new("kern_memfd_noexec",1151 mfd_def_size,1152 MFD_CLOEXEC | MFD_NOEXEC_SEAL);1153 mfd_assert_mode(fd, 0666);1154 mfd_assert_has_seals(fd, F_SEAL_EXEC);1155 mfd_fail_chmod(fd, 0777);1156 close(fd);1157}1158 1159static void test_sysctl_sysctl0(void)1160{1161 int fd;1162 1163 sysctl_assert_equal("0");1164 1165 fd = mfd_assert_new("kern_memfd_sysctl_0_dfl",1166 mfd_def_size,1167 MFD_CLOEXEC | MFD_ALLOW_SEALING);1168 mfd_assert_mode(fd, 0777);1169 mfd_assert_has_seals(fd, 0);1170 mfd_assert_chmod(fd, 0644);1171 close(fd);1172}1173 1174static void test_sysctl_set_sysctl0(void)1175{1176 sysctl_assert_write("0");1177 test_sysctl_sysctl0();1178}1179 1180static void test_sysctl_sysctl1(void)1181{1182 int fd;1183 1184 sysctl_assert_equal("1");1185 1186 fd = mfd_assert_new("kern_memfd_sysctl_1_dfl",1187 mfd_def_size,1188 MFD_CLOEXEC | MFD_ALLOW_SEALING);1189 mfd_assert_mode(fd, 0666);1190 mfd_assert_has_seals(fd, F_SEAL_EXEC);1191 mfd_fail_chmod(fd, 0777);1192 close(fd);1193 1194 fd = mfd_assert_new("kern_memfd_sysctl_1_exec",1195 mfd_def_size,1196 MFD_CLOEXEC | MFD_EXEC | MFD_ALLOW_SEALING);1197 mfd_assert_mode(fd, 0777);1198 mfd_assert_has_seals(fd, 0);1199 mfd_assert_chmod(fd, 0644);1200 close(fd);1201 1202 fd = mfd_assert_new("kern_memfd_sysctl_1_noexec",1203 mfd_def_size,1204 MFD_CLOEXEC | MFD_NOEXEC_SEAL | MFD_ALLOW_SEALING);1205 mfd_assert_mode(fd, 0666);1206 mfd_assert_has_seals(fd, F_SEAL_EXEC);1207 mfd_fail_chmod(fd, 0777);1208 close(fd);1209}1210 1211static void test_sysctl_set_sysctl1(void)1212{1213 sysctl_assert_write("1");1214 test_sysctl_sysctl1();1215}1216 1217static void test_sysctl_sysctl2(void)1218{1219 int fd;1220 1221 sysctl_assert_equal("2");1222 1223 fd = mfd_assert_new("kern_memfd_sysctl_2_dfl",1224 mfd_def_size,1225 MFD_CLOEXEC | MFD_ALLOW_SEALING);1226 mfd_assert_mode(fd, 0666);1227 mfd_assert_has_seals(fd, F_SEAL_EXEC);1228 mfd_fail_chmod(fd, 0777);1229 close(fd);1230 1231 mfd_fail_new("kern_memfd_sysctl_2_exec",1232 MFD_CLOEXEC | MFD_EXEC | MFD_ALLOW_SEALING);1233 1234 fd = mfd_assert_new("kern_memfd_sysctl_2_noexec",1235 mfd_def_size,1236 MFD_CLOEXEC | MFD_NOEXEC_SEAL | MFD_ALLOW_SEALING);1237 mfd_assert_mode(fd, 0666);1238 mfd_assert_has_seals(fd, F_SEAL_EXEC);1239 mfd_fail_chmod(fd, 0777);1240 close(fd);1241}1242 1243static void test_sysctl_set_sysctl2(void)1244{1245 sysctl_assert_write("2");1246 test_sysctl_sysctl2();1247}1248 1249static int sysctl_simple_child(void *arg)1250{1251 printf("%s sysctl 0\n", memfd_str);1252 test_sysctl_set_sysctl0();1253 1254 printf("%s sysctl 1\n", memfd_str);1255 test_sysctl_set_sysctl1();1256 1257 printf("%s sysctl 0\n", memfd_str);1258 test_sysctl_set_sysctl0();1259 1260 printf("%s sysctl 2\n", memfd_str);1261 test_sysctl_set_sysctl2();1262 1263 printf("%s sysctl 1\n", memfd_str);1264 test_sysctl_set_sysctl1();1265 1266 printf("%s sysctl 0\n", memfd_str);1267 test_sysctl_set_sysctl0();1268 1269 return 0;1270}1271 1272/*1273 * Test sysctl1274 * A very basic test to make sure the core sysctl semantics work.1275 */1276static void test_sysctl_simple(void)1277{1278 int pid = spawn_thread(CLONE_NEWPID, sysctl_simple_child, NULL);1279 1280 join_thread(pid);1281}1282 1283static int sysctl_nested(void *arg)1284{1285 void (*fn)(void) = arg;1286 1287 fn();1288 return 0;1289}1290 1291static int sysctl_nested_wait(void *arg)1292{1293 /* Wait for a SIGCONT. */1294 kill(getpid(), SIGSTOP);1295 return sysctl_nested(arg);1296}1297 1298static void test_sysctl_sysctl1_failset(void)1299{1300 sysctl_fail_write("0");1301 test_sysctl_sysctl1();1302}1303 1304static void test_sysctl_sysctl2_failset(void)1305{1306 sysctl_fail_write("1");1307 test_sysctl_sysctl2();1308 1309 sysctl_fail_write("0");1310 test_sysctl_sysctl2();1311}1312 1313static int sysctl_nested_child(void *arg)1314{1315 int pid;1316 1317 printf("%s nested sysctl 0\n", memfd_str);1318 sysctl_assert_write("0");1319 /* A further nested pidns works the same. */1320 pid = spawn_thread(CLONE_NEWPID, sysctl_simple_child, NULL);1321 join_thread(pid);1322 1323 printf("%s nested sysctl 1\n", memfd_str);1324 sysctl_assert_write("1");1325 /* Child inherits our setting. */1326 pid = spawn_thread(CLONE_NEWPID, sysctl_nested, test_sysctl_sysctl1);1327 join_thread(pid);1328 /* Child cannot raise the setting. */1329 pid = spawn_thread(CLONE_NEWPID, sysctl_nested,1330 test_sysctl_sysctl1_failset);1331 join_thread(pid);1332 /* Child can lower the setting. */1333 pid = spawn_thread(CLONE_NEWPID, sysctl_nested,1334 test_sysctl_set_sysctl2);1335 join_thread(pid);1336 /* Child lowering the setting has no effect on our setting. */1337 test_sysctl_sysctl1();1338 1339 printf("%s nested sysctl 2\n", memfd_str);1340 sysctl_assert_write("2");1341 /* Child inherits our setting. */1342 pid = spawn_thread(CLONE_NEWPID, sysctl_nested, test_sysctl_sysctl2);1343 join_thread(pid);1344 /* Child cannot raise the setting. */1345 pid = spawn_thread(CLONE_NEWPID, sysctl_nested,1346 test_sysctl_sysctl2_failset);1347 join_thread(pid);1348 1349 /* Verify that the rules are actually inherited after fork. */1350 printf("%s nested sysctl 0 -> 1 after fork\n", memfd_str);1351 sysctl_assert_write("0");1352 1353 pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait,1354 test_sysctl_sysctl1_failset);1355 sysctl_assert_write("1");1356 kill(pid, SIGCONT);1357 join_thread(pid);1358 1359 printf("%s nested sysctl 0 -> 2 after fork\n", memfd_str);1360 sysctl_assert_write("0");1361 1362 pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait,1363 test_sysctl_sysctl2_failset);1364 sysctl_assert_write("2");1365 kill(pid, SIGCONT);1366 join_thread(pid);1367 1368 /*1369 * Verify that the current effective setting is saved on fork, meaning1370 * that the parent lowering the sysctl doesn't affect already-forked1371 * children.1372 */1373 printf("%s nested sysctl 2 -> 1 after fork\n", memfd_str);1374 sysctl_assert_write("2");1375 pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait,1376 test_sysctl_sysctl2);1377 sysctl_assert_write("1");1378 kill(pid, SIGCONT);1379 join_thread(pid);1380 1381 printf("%s nested sysctl 2 -> 0 after fork\n", memfd_str);1382 sysctl_assert_write("2");1383 pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait,1384 test_sysctl_sysctl2);1385 sysctl_assert_write("0");1386 kill(pid, SIGCONT);1387 join_thread(pid);1388 1389 printf("%s nested sysctl 1 -> 0 after fork\n", memfd_str);1390 sysctl_assert_write("1");1391 pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait,1392 test_sysctl_sysctl1);1393 sysctl_assert_write("0");1394 kill(pid, SIGCONT);1395 join_thread(pid);1396 1397 return 0;1398}1399 1400/*1401 * Test sysctl with nested pid namespaces1402 * Make sure that the sysctl nesting semantics work correctly.1403 */1404static void test_sysctl_nested(void)1405{1406 int pid = spawn_thread(CLONE_NEWPID, sysctl_nested_child, NULL);1407 1408 join_thread(pid);1409}1410 1411/*1412 * Test sharing via dup()1413 * Test that seals are shared between dupped FDs and they're all equal.1414 */1415static void test_share_dup(char *banner, char *b_suffix)1416{1417 int fd, fd2;1418 1419 printf("%s %s %s\n", memfd_str, banner, b_suffix);1420 1421 fd = mfd_assert_new("kern_memfd_share_dup",1422 mfd_def_size,1423 MFD_CLOEXEC | MFD_ALLOW_SEALING);1424 mfd_assert_has_seals(fd, 0);1425 1426 fd2 = mfd_assert_dup(fd);1427 mfd_assert_has_seals(fd2, 0);1428 1429 mfd_assert_add_seals(fd, F_SEAL_WRITE);1430 mfd_assert_has_seals(fd, F_SEAL_WRITE);1431 mfd_assert_has_seals(fd2, F_SEAL_WRITE);1432 1433 mfd_assert_add_seals(fd2, F_SEAL_SHRINK);1434 mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK);1435 mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK);1436 1437 mfd_assert_add_seals(fd, F_SEAL_SEAL);1438 mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL);1439 mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL);1440 1441 mfd_fail_add_seals(fd, F_SEAL_GROW);1442 mfd_fail_add_seals(fd2, F_SEAL_GROW);1443 mfd_fail_add_seals(fd, F_SEAL_SEAL);1444 mfd_fail_add_seals(fd2, F_SEAL_SEAL);1445 1446 close(fd2);1447 1448 mfd_fail_add_seals(fd, F_SEAL_GROW);1449 close(fd);1450}1451 1452/*1453 * Test sealing with active mmap()s1454 * Modifying seals is only allowed if no other mmap() refs exist.1455 */1456static void test_share_mmap(char *banner, char *b_suffix)1457{1458 int fd;1459 void *p;1460 1461 printf("%s %s %s\n", memfd_str, banner, b_suffix);1462 1463 fd = mfd_assert_new("kern_memfd_share_mmap",1464 mfd_def_size,1465 MFD_CLOEXEC | MFD_ALLOW_SEALING);1466 mfd_assert_has_seals(fd, 0);1467 1468 /* shared/writable ref prevents sealing WRITE, but allows others */1469 p = mfd_assert_mmap_shared(fd);1470 mfd_fail_add_seals(fd, F_SEAL_WRITE);1471 mfd_assert_has_seals(fd, 0);1472 mfd_assert_add_seals(fd, F_SEAL_SHRINK);1473 mfd_assert_has_seals(fd, F_SEAL_SHRINK);1474 munmap(p, mfd_def_size);1475 1476 /* readable ref allows sealing */1477 p = mfd_assert_mmap_private(fd);1478 mfd_assert_add_seals(fd, F_SEAL_WRITE);1479 mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK);1480 munmap(p, mfd_def_size);1481 1482 close(fd);1483}1484 1485/*1486 * Test sealing with open(/proc/self/fd/%d)1487 * Via /proc we can get access to a separate file-context for the same memfd.1488 * This is *not* like dup(), but like a real separate open(). Make sure the1489 * semantics are as expected and we correctly check for RDONLY / WRONLY / RDWR.1490 */1491static void test_share_open(char *banner, char *b_suffix)1492{1493 int fd, fd2;1494 1495 printf("%s %s %s\n", memfd_str, banner, b_suffix);1496 1497 fd = mfd_assert_new("kern_memfd_share_open",1498 mfd_def_size,1499 MFD_CLOEXEC | MFD_ALLOW_SEALING);1500 mfd_assert_has_seals(fd, 0);1501 1502 fd2 = mfd_assert_open(fd, O_RDWR, 0);1503 mfd_assert_add_seals(fd, F_SEAL_WRITE);1504 mfd_assert_has_seals(fd, F_SEAL_WRITE);1505 mfd_assert_has_seals(fd2, F_SEAL_WRITE);1506 1507 mfd_assert_add_seals(fd2, F_SEAL_SHRINK);1508 mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK);1509 mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK);1510 1511 close(fd);1512 fd = mfd_assert_open(fd2, O_RDONLY, 0);1513 1514 mfd_fail_add_seals(fd, F_SEAL_SEAL);1515 mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK);1516 mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK);1517 1518 close(fd2);1519 fd2 = mfd_assert_open(fd, O_RDWR, 0);1520 1521 mfd_assert_add_seals(fd2, F_SEAL_SEAL);1522 mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL);1523 mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL);1524 1525 close(fd2);1526 close(fd);1527}1528 1529/*1530 * Test sharing via fork()1531 * Test whether seal-modifications work as expected with forked children.1532 */1533static void test_share_fork(char *banner, char *b_suffix)1534{1535 int fd;1536 pid_t pid;1537 1538 printf("%s %s %s\n", memfd_str, banner, b_suffix);1539 1540 fd = mfd_assert_new("kern_memfd_share_fork",1541 mfd_def_size,1542 MFD_CLOEXEC | MFD_ALLOW_SEALING);1543 mfd_assert_has_seals(fd, 0);1544 1545 pid = spawn_idle_thread(0);1546 mfd_assert_add_seals(fd, F_SEAL_SEAL);1547 mfd_assert_has_seals(fd, F_SEAL_SEAL);1548 1549 mfd_fail_add_seals(fd, F_SEAL_WRITE);1550 mfd_assert_has_seals(fd, F_SEAL_SEAL);1551 1552 join_idle_thread(pid);1553 1554 mfd_fail_add_seals(fd, F_SEAL_WRITE);1555 mfd_assert_has_seals(fd, F_SEAL_SEAL);1556 1557 close(fd);1558}1559 1560int main(int argc, char **argv)1561{1562 pid_t pid;1563 1564 if (argc == 2) {1565 if (!strcmp(argv[1], "hugetlbfs")) {1566 unsigned long hpage_size = default_huge_page_size();1567 1568 if (!hpage_size) {1569 printf("Unable to determine huge page size\n");1570 abort();1571 }1572 1573 hugetlbfs_test = 1;1574 memfd_str = MEMFD_HUGE_STR;1575 mfd_def_size = hpage_size * 2;1576 } else {1577 printf("Unknown option: %s\n", argv[1]);1578 abort();1579 }1580 }1581 1582 test_create();1583 test_basic();1584 test_exec_seal();1585 test_exec_no_seal();1586 test_noexec_seal();1587 1588 test_seal_write();1589 test_seal_future_write();1590 test_seal_shrink();1591 test_seal_grow();1592 test_seal_resize();1593 1594 test_sysctl_simple();1595 test_sysctl_nested();1596 1597 test_share_dup("SHARE-DUP", "");1598 test_share_mmap("SHARE-MMAP", "");1599 test_share_open("SHARE-OPEN", "");1600 test_share_fork("SHARE-FORK", "");1601 1602 /* Run test-suite in a multi-threaded environment with a shared1603 * file-table. */1604 pid = spawn_idle_thread(CLONE_FILES | CLONE_FS | CLONE_VM);1605 test_share_dup("SHARE-DUP", SHARED_FT_STR);1606 test_share_mmap("SHARE-MMAP", SHARED_FT_STR);1607 test_share_open("SHARE-OPEN", SHARED_FT_STR);1608 test_share_fork("SHARE-FORK", SHARED_FT_STR);1609 join_idle_thread(pid);1610 1611 printf("memfd: DONE\n");1612 1613 return 0;1614}1615