Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 25 Sep 2019 18:03:18 +0000 (UTC)
From:      Kyle Evans <kevans@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r352703 - in head: . lib/libc/include lib/libc/sys sys/sys tests/sys/kern
Message-ID:  <201909251803.x8PI3IsX082662@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kevans
Date: Wed Sep 25 18:03:18 2019
New Revision: 352703
URL: https://svnweb.freebsd.org/changeset/base/352703

Log:
  Add linux-compatible memfd_create
  
  memfd_create is effectively a SHM_ANON shm_open(2) mapping with optional
  CLOEXEC and file sealing support. This is used by some mesa parts, some
  linux libs, and qemu can also take advantage of it and uses the sealing to
  prevent resizing the region.
  
  This reimplements shm_open in terms of shm_open2(2) at the same time.
  
  shm_open(2) will be moved to COMPAT12 shortly.
  
  Reviewed by:	markj, kib
  Differential Revision:	https://reviews.freebsd.org/D21393

Added:
  head/lib/libc/sys/shm_open.c   (contents, props changed)
  head/tests/sys/kern/memfd_test.c   (contents, props changed)
Modified:
  head/Makefile.inc1
  head/lib/libc/include/libc_private.h
  head/lib/libc/sys/Makefile.inc
  head/lib/libc/sys/Symbol.map
  head/lib/libc/sys/shm_open.2
  head/sys/sys/mman.h
  head/tests/sys/kern/Makefile

Modified: head/Makefile.inc1
==============================================================================
--- head/Makefile.inc1	Wed Sep 25 18:03:15 2019	(r352702)
+++ head/Makefile.inc1	Wed Sep 25 18:03:18 2019	(r352703)
@@ -947,7 +947,8 @@ _cleanobj_fast_depend_hack: .PHONY
 # Syscall stubs rewritten in C and obsolete MD assembly implementations
 # Date      SVN Rev  Syscalls
 # 20180604  r334626  brk sbrk
-.for f in brk sbrk
+# 20190916  r35XXXX  shm_open
+.for f in brk sbrk shm_open
 	@if [ -e "${OBJTOP}/lib/libc/.depend.${f}.o" ] && \
 	    egrep -qw '${f}\.[sS]' ${OBJTOP}/lib/libc/.depend.${f}.o; then \
 		echo "Removing stale dependencies for ${f} syscall wrappers"; \

Modified: head/lib/libc/include/libc_private.h
==============================================================================
--- head/lib/libc/include/libc_private.h	Wed Sep 25 18:03:15 2019	(r352702)
+++ head/lib/libc/include/libc_private.h	Wed Sep 25 18:03:18 2019	(r352703)
@@ -391,6 +391,7 @@ __pid_t		__sys_wait6(enum idtype, __id_t, int *, int,
 		    struct __wrusage *, struct __siginfo *);
 __ssize_t	__sys_write(int, const void *, __size_t);
 __ssize_t	__sys_writev(int, const struct iovec *, int);
+int		__sys_shm_open2(const char *, int, __mode_t, int, const char *);
 
 int		__libc_sigaction(int, const struct sigaction *,
 		    struct sigaction *) __hidden;

Modified: head/lib/libc/sys/Makefile.inc
==============================================================================
--- head/lib/libc/sys/Makefile.inc	Wed Sep 25 18:03:15 2019	(r352702)
+++ head/lib/libc/sys/Makefile.inc	Wed Sep 25 18:03:18 2019	(r352703)
@@ -46,6 +46,7 @@ PSEUDO+= _getdirentries.o
 
 SRCS+= brk.c
 SRCS+= pipe.c
+SRCS+= shm_open.c
 SRCS+= vadvise.c
 
 SRCS+=	compat-stub.c
@@ -475,7 +476,8 @@ MLINKS+=setuid.2 setegid.2 \
 	setuid.2 seteuid.2 \
 	setuid.2 setgid.2
 MLINKS+=shmat.2 shmdt.2
-MLINKS+=shm_open.2 shm_unlink.2
+MLINKS+=shm_open.2 memfd_create.3 \
+	shm_open.2 shm_unlink.2
 MLINKS+=sigwaitinfo.2 sigtimedwait.2
 MLINKS+=stat.2 fstat.2 \
 	stat.2 fstatat.2 \

Modified: head/lib/libc/sys/Symbol.map
==============================================================================
--- head/lib/libc/sys/Symbol.map	Wed Sep 25 18:03:15 2019	(r352702)
+++ head/lib/libc/sys/Symbol.map	Wed Sep 25 18:03:18 2019	(r352703)
@@ -409,6 +409,7 @@ FBSD_1.6 {
 	fhreadlink;
 	getfhat;
 	funlinkat;
+	memfd_create;
 };
 
 FBSDprivate_1.0 {

Modified: head/lib/libc/sys/shm_open.2
==============================================================================
--- head/lib/libc/sys/shm_open.2	Wed Sep 25 18:03:15 2019	(r352702)
+++ head/lib/libc/sys/shm_open.2	Wed Sep 25 18:03:18 2019	(r352703)
@@ -28,11 +28,11 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd January 20, 2017
+.Dd September 24, 2019
 .Dt SHM_OPEN 2
 .Os
 .Sh NAME
-.Nm shm_open , shm_unlink
+.Nm memfd_create , shm_open , shm_unlink
 .Nd "shared memory object operations"
 .Sh LIBRARY
 .Lb libc
@@ -41,6 +41,8 @@
 .In sys/mman.h
 .In fcntl.h
 .Ft int
+.Fn memfd_create "const char *name" "unsigned int flags"
+.Ft int
 .Fn shm_open "const char *path" "int flags" "mode_t mode"
 .Ft int
 .Fn shm_unlink "const char *path"
@@ -139,14 +141,64 @@ The
 .Fn shm_unlink
 system call removes a shared memory object named
 .Fa path .
+.Pp
+The
+.Fn memfd_create
+function creates an anonymous shared memory object, identical to that created
+by
+.Fn shm_open
+when
+.Dv SHM_ANON
+is specified.
+Newly created objects start off with a size of zero.
+The size of the new object must be adjusted via
+.Xr ftruncate 2 .
+.Pp
+The
+.Fa name
+argument must not be
+.Dv NULL ,
+but it may be an empty string.
+The length of the
+.Fa name
+argument may not exceed
+.Dv NAME_MAX
+minus six characters for the prefix
+.Dq memfd: ,
+which will be prepended.
+The
+.Fa name
+argument is intended solely for debugging purposes and will never be used by the
+kernel to identify a memfd.
+Names are therefore not required to be unique.
+.Pp
+The following
+.Fa flags
+may be specified to
+.Fn memfd_create :
+.Bl -tag -width MFD_ALLOW_SEALING
+.It Dv MFD_CLOEXEC
+Set
+.Dv FD_CLOEXEC
+on the resulting file descriptor.
+.It Dv MFD_ALLOW_SEALING
+Allow adding seals to the resulting file descriptor using the
+.Dv F_ADD_SEALS
+.Xr fcntl 2
+command.
+.It Dv MFD_HUGETLB
+This flag is currently unsupported.
+.El
 .Sh RETURN VALUES
 If successful,
+.Fn memfd_create
+and
 .Fn shm_open
-returns a non-negative integer,
+both return a non-negative integer,
 and
 .Fn shm_unlink
 returns zero.
-Both functions return -1 on failure, and set
+All three functions return -1 on failure, and set
 .Va errno
 to indicate the error.
 .Sh COMPATIBILITY
@@ -220,6 +272,33 @@ This example fails without the call to
                 errx(EX_IOERR, "%s: pwrite length mismatch", __func__);
 .Ed
 .Sh ERRORS
+.Fn memfd_create
+fails with these error codes for these conditions:
+.Bl -tag -width Er
+.It Bq Er EBADF
+The
+.Fa name
+argument was NULL.
+.It Bq Er EINVAL
+The
+.Fa name
+argument was too long.
+.Pp
+An invalid or unsupported flag was included in
+.Fa flags .
+.It Bq Er EMFILE
+The process has already reached its limit for open file descriptors.
+.It Bq Er ENFILE
+The system file table is full.
+.It Bq Er ENOSYS
+In
+.Fa memfd_create ,
+.Dv MFD_HUGETLB
+was specified in
+.Fa flags ,
+and this system does not support forced hugetlb mappings.
+.El
+.Pp
 .Fn shm_open
 fails with these error codes for these conditions:
 .Bl -tag -width Er
@@ -290,12 +369,22 @@ requires write permission to the shared memory object.
 .Xr sendfile 2
 .Sh STANDARDS
 The
+.Fn memfd_create
+function is expected to be compatible with the Linux system call of the same
+name.
+.Pp
+The
 .Fn shm_open
 and
 .Fn shm_unlink
 functions are believed to conform to
 .St -p1003.1b-93 .
 .Sh HISTORY
+The
+.Fn memfd_create
+function appeared in
+.Fx 13.0 .
+.Pp
 The
 .Fn shm_open
 and

Added: head/lib/libc/sys/shm_open.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libc/sys/shm_open.c	Wed Sep 25 18:03:18 2019	(r352703)
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libc_private.h"
+
+__weak_reference(shm_open, _shm_open);
+__weak_reference(shm_open, __sys_shm_open);
+
+#ifndef SYS_freebsd12_shm_open
+#define	SYS_freebsd12_shm_open	SYS_shm_open
+#endif
+
+#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));
+}

Modified: head/sys/sys/mman.h
==============================================================================
--- head/sys/sys/mman.h	Wed Sep 25 18:03:15 2019	(r352702)
+++ head/sys/sys/mman.h	Wed Sep 25 18:03:18 2019	(r352703)
@@ -182,6 +182,30 @@
  */
 #define	SHM_ALLOW_SEALING		0x00000001
 
+/*
+ * Flags for memfd_create().
+ */
+#define	MFD_ALLOW_SEALING		0x00000001
+#define	MFD_CLOEXEC			0x00000002
+
+/* UNSUPPORTED */
+#define	MFD_HUGETLB			0x00000004
+
+#define	MFD_HUGE_MASK			0xFC000000
+#define	MFD_HUGE_SHIFT			26
+#define	MFD_HUGE_64KB			(16 << MFD_HUGE_SHIFT)
+#define	MFD_HUGE_512KB			(19 << MFD_HUGE_SHIFT)
+#define	MFD_HUGE_1MB			(20 << MFD_HUGE_SHIFT)
+#define	MFD_HUGE_2MB			(21 << MFD_HUGE_SHIFT)
+#define	MFD_HUGE_8MB			(23 << MFD_HUGE_SHIFT)
+#define	MFD_HUGE_16MB			(24 << MFD_HUGE_SHIFT)
+#define	MFD_HUGE_32MB			(25 << MFD_HUGE_SHIFT)
+#define	MFD_HUGE_256MB			(28 << MFD_HUGE_SHIFT)
+#define	MFD_HUGE_512MB			(29 << MFD_HUGE_SHIFT)
+#define	MFD_HUGE_1GB			(30 << MFD_HUGE_SHIFT)
+#define	MFD_HUGE_2GB			(31 << MFD_HUGE_SHIFT)
+#define	MFD_HUGE_16GB			(34 << MFD_HUGE_SHIFT)
+
 #endif /* __BSD_VISIBLE */
 
 /*
@@ -290,6 +314,9 @@ int	mlockall(int);
 int	munlockall(void);
 int	shm_open(const char *, int, mode_t);
 int	shm_unlink(const char *);
+#endif
+#if __BSD_VISIBLE
+int	memfd_create(const char *, unsigned int);
 #endif
 __END_DECLS
 

Modified: head/tests/sys/kern/Makefile
==============================================================================
--- head/tests/sys/kern/Makefile	Wed Sep 25 18:03:15 2019	(r352702)
+++ head/tests/sys/kern/Makefile	Wed Sep 25 18:03:18 2019	(r352703)
@@ -9,6 +9,7 @@ TESTSDIR=	${TESTSBASE}/sys/kern
 ATF_TESTS_C+=	kern_copyin
 ATF_TESTS_C+=	kern_descrip_test
 ATF_TESTS_C+=	kill_zombie
+ATF_TESTS_C+=	memfd_test
 ATF_TESTS_C+=	ptrace_test
 TEST_METADATA.ptrace_test+=		timeout="15"
 ATF_TESTS_C+=	reaper

Added: head/tests/sys/kern/memfd_test.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/tests/sys/kern/memfd_test.c	Wed Sep 25 18:03:18 2019	(r352703)
@@ -0,0 +1,276 @@
+/*-
+ * Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <unistd.h>
+
+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());
+}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201909251803.x8PI3IsX082662>