Index: head/lib/libc/sys/shm_open.c =================================================================== --- head/lib/libc/sys/shm_open.c (revision 352726) +++ head/lib/libc/sys/shm_open.c (revision 352727) @@ -1,109 +1,111 @@ /* + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2019 Kyle Evans * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice(s), this list of conditions and the following disclaimer as * the first lines of this file unmodified other than the possible * addition of one or more copyright notices. * 2. Redistributions in binary form must reproduce the above copyright * notice(s), this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "libc_private.h" __weak_reference(shm_open, _shm_open); __weak_reference(shm_open, __sys_shm_open); #define SHM_OPEN2_OSREL 1300048 #define MEMFD_NAME_PREFIX "memfd:" int shm_open(const char *path, int flags, mode_t mode) { if (__getosreldate() >= SHM_OPEN2_OSREL) return (__sys_shm_open2(path, flags | O_CLOEXEC, mode, 0, NULL)); /* * Fallback to shm_open(2) on older kernels. The kernel will enforce * O_CLOEXEC in this interface, unlike the newer shm_open2 which does * not enforce it. The newer interface allows memfd_create(), for * instance, to not have CLOEXEC on the returned fd. */ return (syscall(SYS_freebsd12_shm_open, path, flags, mode)); } /* * The path argument is passed to the kernel, but the kernel doesn't currently * do anything with it. Linux exposes it in linprocfs for debugging purposes * only, but our kernel currently will not do the same. */ int memfd_create(const char *name, unsigned int flags) { char memfd_name[NAME_MAX + 1]; size_t namelen; int oflags, shmflags; if (name == NULL) return (EBADF); namelen = strlen(name); if (namelen + sizeof(MEMFD_NAME_PREFIX) - 1 > NAME_MAX) return (EINVAL); if ((flags & ~(MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_HUGETLB | MFD_HUGE_MASK)) != 0) return (EINVAL); /* HUGETLB set with no size specified. */ if ((flags & MFD_HUGETLB) != 0 && (flags & MFD_HUGE_MASK) == 0) return (EINVAL); /* Size specified but no HUGETLB. */ if ((flags & MFD_HUGE_MASK) != 0 && (flags & MFD_HUGETLB) == 0) return (EINVAL); /* We don't actually support HUGETLB. */ if ((flags & MFD_HUGETLB) != 0) return (ENOSYS); /* We've already validated that we're sufficiently sized. */ snprintf(memfd_name, NAME_MAX + 1, "%s%s", MEMFD_NAME_PREFIX, name); oflags = O_RDWR; shmflags = 0; if ((flags & MFD_CLOEXEC) != 0) oflags |= O_CLOEXEC; if ((flags & MFD_ALLOW_SEALING) != 0) shmflags |= SHM_ALLOW_SEALING; return (__sys_shm_open2(SHM_ANON, oflags, 0, shmflags, memfd_name)); } Index: head/tests/sys/kern/memfd_test.c =================================================================== --- head/tests/sys/kern/memfd_test.c (revision 352726) +++ head/tests/sys/kern/memfd_test.c (revision 352727) @@ -1,276 +1,278 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2019 Kyle Evans * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include ATF_TC_WITHOUT_HEAD(basic); ATF_TC_BODY(basic, tc) { int fd; char buf[8]; ATF_REQUIRE((fd = memfd_create("...", 0)) != -1); /* File size should be initially 0 */ ATF_REQUIRE(write(fd, buf, sizeof(buf)) == 0); /* ftruncate(2) must succeed without seals */ ATF_REQUIRE(ftruncate(fd, sizeof(buf) - 1) == 0); ATF_REQUIRE(write(fd, buf, sizeof(buf)) == sizeof(buf) - 1); close(fd); } ATF_TC_WITHOUT_HEAD(cloexec); ATF_TC_BODY(cloexec, tc) { int fd_nocl, fd_cl; ATF_REQUIRE((fd_nocl = memfd_create("...", 0)) != -1); ATF_REQUIRE((fd_cl = memfd_create("...", MFD_CLOEXEC)) != -1); ATF_REQUIRE((fcntl(fd_nocl, F_GETFD) & FD_CLOEXEC) == 0); ATF_REQUIRE((fcntl(fd_cl, F_GETFD) & FD_CLOEXEC) != 0); close(fd_nocl); close(fd_cl); } ATF_TC_WITHOUT_HEAD(disallowed_sealing); ATF_TC_BODY(disallowed_sealing, tc) { int fd; ATF_REQUIRE((fd = memfd_create("...", 0)) != -1); ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == F_SEAL_SEAL); ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1); ATF_REQUIRE(errno == EPERM); close(fd); } #define BUF_SIZE 1024 ATF_TC_WITHOUT_HEAD(write_seal); ATF_TC_BODY(write_seal, tc) { int fd; char *addr, buf[BUF_SIZE]; ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1); ATF_REQUIRE(ftruncate(fd, BUF_SIZE) == 0); /* Write once, then we'll seal it and try again */ ATF_REQUIRE(write(fd, buf, BUF_SIZE) == BUF_SIZE); ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0); addr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0); ATF_REQUIRE(addr != MAP_FAILED); ATF_REQUIRE(munmap(addr, BUF_SIZE) == 0); ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == 0); ATF_REQUIRE(write(fd, buf, BUF_SIZE) == -1); ATF_REQUIRE(errno == EPERM); ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0) == MAP_FAILED); ATF_REQUIRE(errno == EPERM); close(fd); } ATF_TC_WITHOUT_HEAD(mmap_write_seal); ATF_TC_BODY(mmap_write_seal, tc) { int fd; char *addr, *paddr, *raddr; ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1); ATF_REQUIRE(ftruncate(fd, BUF_SIZE) == 0); /* Map it, both shared and privately */ addr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0); ATF_REQUIRE(addr != MAP_FAILED); paddr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0); ATF_REQUIRE(paddr != MAP_FAILED); raddr = mmap(0, BUF_SIZE, PROT_READ, MAP_SHARED, fd, 0); ATF_REQUIRE(raddr != MAP_FAILED); /* Now try to seal it before unmapping */ ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1); ATF_REQUIRE(errno == EBUSY); ATF_REQUIRE(munmap(addr, BUF_SIZE) == 0); ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == 0); ATF_REQUIRE(munmap(paddr, BUF_SIZE) == 0); ATF_REQUIRE(munmap(raddr, BUF_SIZE) == 0); ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0) == MAP_FAILED); ATF_REQUIRE(errno == EPERM); paddr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0); ATF_REQUIRE(paddr != MAP_FAILED); raddr = mmap(0, BUF_SIZE, PROT_READ, MAP_SHARED, fd, 0); ATF_REQUIRE(raddr != MAP_FAILED); ATF_REQUIRE(munmap(raddr, BUF_SIZE) == 0); ATF_REQUIRE(munmap(paddr, BUF_SIZE) == 0); close(fd); } static int memfd_truncate_test(int initial_size, int dest_size, int seals) { int err, fd; ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1); ATF_REQUIRE(ftruncate(fd, initial_size) == 0); ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, seals) == 0); err = ftruncate(fd, dest_size); if (err != 0) err = errno; close(fd); return (err); } ATF_TC_WITHOUT_HEAD(truncate_seals); ATF_TC_BODY(truncate_seals, tc) { ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_GROW) == EPERM); ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_SHRINK) == EPERM); ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_GROW) == 0); ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_SHRINK) == 0); ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_GROW | F_SEAL_SHRINK) == EPERM); ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_GROW | F_SEAL_SHRINK) == EPERM); ATF_REQUIRE(memfd_truncate_test(4, 4, F_SEAL_GROW | F_SEAL_SHRINK) == 0); } ATF_TC_WITHOUT_HEAD(get_seals); ATF_TC_BODY(get_seals, tc) { int fd; int seals; ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1); ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == 0); ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW) == 0); seals = fcntl(fd, F_GET_SEALS); ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW)); close(fd); } ATF_TC_WITHOUT_HEAD(dup_seals); ATF_TC_BODY(dup_seals, tc) { char buf[8]; int fd, fdx; int seals; ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1); ATF_REQUIRE((fdx = dup(fd)) != -1); ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == 0); ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW) == 0); seals = fcntl(fd, F_GET_SEALS); ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW)); seals = fcntl(fdx, F_GET_SEALS); ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW)); /* Make sure the seal's actually being applied at the inode level */ ATF_REQUIRE(write(fdx, buf, sizeof(buf)) == -1); ATF_REQUIRE(errno == EPERM); ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED, fdx, 0) == MAP_FAILED); ATF_REQUIRE(errno == EPERM); close(fd); close(fdx); } ATF_TC_WITHOUT_HEAD(immutable_seals); ATF_TC_BODY(immutable_seals, tc) { int fd; ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1); ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_SEAL) == 0); ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW) == -1); ATF_REQUIRE_MSG(errno == EPERM, "Added unique grow seal after restricting seals"); close(fd); /* * Also check that adding a seal that already exists really doesn't * do anything once we're sealed. */ ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1); ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SEAL) == 0); ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW) == -1); ATF_REQUIRE_MSG(errno == EPERM, "Added duplicate grow seal after restricting seals"); close(fd); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, basic); ATF_TP_ADD_TC(tp, cloexec); ATF_TP_ADD_TC(tp, disallowed_sealing); ATF_TP_ADD_TC(tp, write_seal); ATF_TP_ADD_TC(tp, mmap_write_seal); ATF_TP_ADD_TC(tp, truncate_seals); ATF_TP_ADD_TC(tp, get_seals); ATF_TP_ADD_TC(tp, dup_seals); ATF_TP_ADD_TC(tp, immutable_seals); return (atf_no_error()); }