xref: /freebsd-src/tests/sys/posixshm/memfd_test.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
193900fe7SKyle Evans /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
393900fe7SKyle Evans  *
493900fe7SKyle Evans  * Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
593900fe7SKyle Evans  *
693900fe7SKyle Evans  * Redistribution and use in source and binary forms, with or without
793900fe7SKyle Evans  * modification, are permitted provided that the following conditions
893900fe7SKyle Evans  * are met:
993900fe7SKyle Evans  * 1. Redistributions of source code must retain the above copyright
1093900fe7SKyle Evans  *    notice, this list of conditions and the following disclaimer.
1193900fe7SKyle Evans  * 2. Redistributions in binary form must reproduce the above copyright
1293900fe7SKyle Evans  *    notice, this list of conditions and the following disclaimer in the
1393900fe7SKyle Evans  *    documentation and/or other materials provided with the distribution.
1493900fe7SKyle Evans  *
1593900fe7SKyle Evans  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1693900fe7SKyle Evans  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1793900fe7SKyle Evans  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1893900fe7SKyle Evans  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1993900fe7SKyle Evans  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2093900fe7SKyle Evans  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2193900fe7SKyle Evans  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2293900fe7SKyle Evans  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2393900fe7SKyle Evans  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2493900fe7SKyle Evans  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2593900fe7SKyle Evans  * SUCH DAMAGE.
2693900fe7SKyle Evans  */
2793900fe7SKyle Evans 
2893900fe7SKyle Evans #include <sys/cdefs.h>
2993900fe7SKyle Evans #include <sys/fcntl.h>
3093900fe7SKyle Evans #include <sys/mman.h>
3193900fe7SKyle Evans #include <sys/stat.h>
3293900fe7SKyle Evans 
3393900fe7SKyle Evans #include <atf-c.h>
3493900fe7SKyle Evans #include <errno.h>
3593900fe7SKyle Evans #include <unistd.h>
3693900fe7SKyle Evans 
3793900fe7SKyle Evans ATF_TC_WITHOUT_HEAD(basic);
ATF_TC_BODY(basic,tc)3893900fe7SKyle Evans ATF_TC_BODY(basic, tc)
3993900fe7SKyle Evans {
4093900fe7SKyle Evans 	struct stat sb;
4193900fe7SKyle Evans 	int fd;
4293900fe7SKyle Evans 	char buf[8];
4393900fe7SKyle Evans 
4493900fe7SKyle Evans 	ATF_REQUIRE((fd = memfd_create("...", 0)) != -1);
4593900fe7SKyle Evans 
4693900fe7SKyle Evans 	/* write(2) should grow us out automatically. */
4793900fe7SKyle Evans 	ATF_REQUIRE(write(fd, buf, sizeof(buf)) == sizeof(buf));
4893900fe7SKyle Evans 	ATF_REQUIRE(fstat(fd, &sb) == 0);
4993900fe7SKyle Evans 	ATF_REQUIRE(sb.st_size == sizeof(buf));
5093900fe7SKyle Evans 
5193900fe7SKyle Evans 	/* ftruncate(2) must succeed without seals */
5293900fe7SKyle Evans 	ATF_REQUIRE(ftruncate(fd, 2 * (sizeof(buf) - 1)) == 0);
5393900fe7SKyle Evans 
5493900fe7SKyle Evans 	/* write(2) again must not be limited by ftruncate(2) size. */
5593900fe7SKyle Evans 	ATF_REQUIRE(write(fd, buf, sizeof(buf)) == sizeof(buf));
5693900fe7SKyle Evans 
5793900fe7SKyle Evans 	/* Sanity check. */
5893900fe7SKyle Evans 	ATF_REQUIRE(fstat(fd, &sb) == 0);
5993900fe7SKyle Evans 	ATF_REQUIRE(sb.st_size == 2 * sizeof(buf));
6093900fe7SKyle Evans 
6193900fe7SKyle Evans 	close(fd);
6293900fe7SKyle Evans }
6393900fe7SKyle Evans 
6493900fe7SKyle Evans ATF_TC_WITHOUT_HEAD(cloexec);
ATF_TC_BODY(cloexec,tc)6593900fe7SKyle Evans ATF_TC_BODY(cloexec, tc)
6693900fe7SKyle Evans {
6793900fe7SKyle Evans 	int fd_nocl, fd_cl;
6893900fe7SKyle Evans 
6993900fe7SKyle Evans 	ATF_REQUIRE((fd_nocl = memfd_create("...", 0)) != -1);
7093900fe7SKyle Evans 	ATF_REQUIRE((fd_cl = memfd_create("...", MFD_CLOEXEC)) != -1);
7193900fe7SKyle Evans 
7293900fe7SKyle Evans 	ATF_REQUIRE((fcntl(fd_nocl, F_GETFD) & FD_CLOEXEC) == 0);
7393900fe7SKyle Evans 	ATF_REQUIRE((fcntl(fd_cl, F_GETFD) & FD_CLOEXEC) != 0);
7493900fe7SKyle Evans 
7593900fe7SKyle Evans 	close(fd_nocl);
7693900fe7SKyle Evans 	close(fd_cl);
7793900fe7SKyle Evans }
7893900fe7SKyle Evans 
7993900fe7SKyle Evans ATF_TC_WITHOUT_HEAD(disallowed_sealing);
ATF_TC_BODY(disallowed_sealing,tc)8093900fe7SKyle Evans ATF_TC_BODY(disallowed_sealing, tc)
8193900fe7SKyle Evans {
8293900fe7SKyle Evans 	int fd;
8393900fe7SKyle Evans 
8493900fe7SKyle Evans 	ATF_REQUIRE((fd = memfd_create("...", 0)) != -1);
8593900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == F_SEAL_SEAL);
8693900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1);
8793900fe7SKyle Evans 	ATF_REQUIRE(errno == EPERM);
8893900fe7SKyle Evans 
8993900fe7SKyle Evans 	close(fd);
9093900fe7SKyle Evans }
9193900fe7SKyle Evans 
9293900fe7SKyle Evans #define	BUF_SIZE	1024
9393900fe7SKyle Evans 
9493900fe7SKyle Evans ATF_TC_WITHOUT_HEAD(write_seal);
ATF_TC_BODY(write_seal,tc)9593900fe7SKyle Evans ATF_TC_BODY(write_seal, tc)
9693900fe7SKyle Evans {
9793900fe7SKyle Evans 	int fd;
9893900fe7SKyle Evans 	char *addr, buf[BUF_SIZE];
9993900fe7SKyle Evans 
10093900fe7SKyle Evans 	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
10193900fe7SKyle Evans 	ATF_REQUIRE(ftruncate(fd, BUF_SIZE) == 0);
10293900fe7SKyle Evans 
10393900fe7SKyle Evans 	/* Write once, then we'll seal it and try again */
10493900fe7SKyle Evans 	ATF_REQUIRE(write(fd, buf, BUF_SIZE) == BUF_SIZE);
10593900fe7SKyle Evans 	ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0);
10693900fe7SKyle Evans 
10793900fe7SKyle Evans 	addr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0);
10893900fe7SKyle Evans 	ATF_REQUIRE(addr != MAP_FAILED);
10993900fe7SKyle Evans 	ATF_REQUIRE(munmap(addr, BUF_SIZE) == 0);
11093900fe7SKyle Evans 
11193900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == 0);
11293900fe7SKyle Evans 
11393900fe7SKyle Evans 	ATF_REQUIRE(write(fd, buf, BUF_SIZE) == -1);
11493900fe7SKyle Evans 	ATF_REQUIRE(errno == EPERM);
11593900fe7SKyle Evans 
11693900fe7SKyle Evans 	ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED,
11793900fe7SKyle Evans 	    fd, 0) == MAP_FAILED);
11893900fe7SKyle Evans 	ATF_REQUIRE(errno == EACCES);
11993900fe7SKyle Evans 
12093900fe7SKyle Evans 	close(fd);
12193900fe7SKyle Evans }
12293900fe7SKyle Evans 
12393900fe7SKyle Evans ATF_TC_WITHOUT_HEAD(mmap_write_seal);
ATF_TC_BODY(mmap_write_seal,tc)12493900fe7SKyle Evans ATF_TC_BODY(mmap_write_seal, tc)
12593900fe7SKyle Evans {
12693900fe7SKyle Evans 	int fd;
12793900fe7SKyle Evans 	char *addr, *paddr, *raddr;
12893900fe7SKyle Evans 
12993900fe7SKyle Evans 	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
13093900fe7SKyle Evans 	ATF_REQUIRE(ftruncate(fd, BUF_SIZE) == 0);
13193900fe7SKyle Evans 
13293900fe7SKyle Evans 	/* Map it, both shared and privately */
13393900fe7SKyle Evans 	addr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0);
13493900fe7SKyle Evans 	ATF_REQUIRE(addr != MAP_FAILED);
13593900fe7SKyle Evans 	paddr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0);
13693900fe7SKyle Evans 	ATF_REQUIRE(paddr != MAP_FAILED);
13793900fe7SKyle Evans 	raddr = mmap(0, BUF_SIZE, PROT_READ, MAP_SHARED, fd, 0);
13893900fe7SKyle Evans 	ATF_REQUIRE(raddr != MAP_FAILED);
13993900fe7SKyle Evans 
14093900fe7SKyle Evans 	/* Now try to seal it before unmapping */
14193900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1);
14293900fe7SKyle Evans 	ATF_REQUIRE(errno == EBUSY);
14393900fe7SKyle Evans 
14493900fe7SKyle Evans 	ATF_REQUIRE(munmap(addr, BUF_SIZE) == 0);
14593900fe7SKyle Evans 
14693900fe7SKyle Evans 	/*
14793900fe7SKyle Evans 	 * This should fail, because raddr still exists and it was spawned from
14893900fe7SKyle Evans 	 * a r/w fd.
14993900fe7SKyle Evans 	 */
15093900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1);
15193900fe7SKyle Evans 	ATF_REQUIRE(errno == EBUSY);
15293900fe7SKyle Evans 
15393900fe7SKyle Evans 	ATF_REQUIRE(munmap(raddr, BUF_SIZE) == 0);
15493900fe7SKyle Evans 	/* This one should succeed; only the private mapping remains. */
15593900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == 0);
15693900fe7SKyle Evans 
15793900fe7SKyle Evans 	ATF_REQUIRE(munmap(paddr, BUF_SIZE) == 0);
15893900fe7SKyle Evans 	ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED,
15993900fe7SKyle Evans 	    fd, 0) == MAP_FAILED);
16093900fe7SKyle Evans 	ATF_REQUIRE(errno == EACCES);
16193900fe7SKyle Evans 
16293900fe7SKyle Evans 	/* Make sure we can still map privately r/w or shared r/o. */
16393900fe7SKyle Evans 	paddr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0);
16493900fe7SKyle Evans 	ATF_REQUIRE(paddr != MAP_FAILED);
16593900fe7SKyle Evans 	raddr = mmap(0, BUF_SIZE, PROT_READ, MAP_SHARED, fd, 0);
16693900fe7SKyle Evans 	ATF_REQUIRE(raddr != MAP_FAILED);
16793900fe7SKyle Evans 	ATF_REQUIRE(munmap(raddr, BUF_SIZE) == 0);
16893900fe7SKyle Evans 	ATF_REQUIRE(munmap(paddr, BUF_SIZE) == 0);
16993900fe7SKyle Evans 
17093900fe7SKyle Evans 	close(fd);
17193900fe7SKyle Evans }
17293900fe7SKyle Evans 
17393900fe7SKyle Evans static int
memfd_truncate_test(int initial_size,int dest_size,int seals)17493900fe7SKyle Evans memfd_truncate_test(int initial_size, int dest_size, int seals)
17593900fe7SKyle Evans {
17693900fe7SKyle Evans 	int err, fd;
17793900fe7SKyle Evans 
17893900fe7SKyle Evans 	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
17993900fe7SKyle Evans 	ATF_REQUIRE(ftruncate(fd, initial_size) == 0);
18093900fe7SKyle Evans 
18193900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, seals) == 0);
18293900fe7SKyle Evans 
18393900fe7SKyle Evans 	err = ftruncate(fd, dest_size);
18493900fe7SKyle Evans 	if (err != 0)
18593900fe7SKyle Evans 		err = errno;
18693900fe7SKyle Evans 	close(fd);
18793900fe7SKyle Evans 	return (err);
18893900fe7SKyle Evans }
18993900fe7SKyle Evans 
19093900fe7SKyle Evans ATF_TC_WITHOUT_HEAD(truncate_seals);
ATF_TC_BODY(truncate_seals,tc)19193900fe7SKyle Evans ATF_TC_BODY(truncate_seals, tc)
19293900fe7SKyle Evans {
19393900fe7SKyle Evans 
19493900fe7SKyle Evans 	ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_GROW) == EPERM);
19593900fe7SKyle Evans 	ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_SHRINK) == EPERM);
19693900fe7SKyle Evans 	ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_GROW) == 0);
19793900fe7SKyle Evans 	ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_SHRINK) == 0);
19893900fe7SKyle Evans 
19993900fe7SKyle Evans 	ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_GROW | F_SEAL_SHRINK) ==
20093900fe7SKyle Evans 	    EPERM);
20193900fe7SKyle Evans 	ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_GROW | F_SEAL_SHRINK) ==
20293900fe7SKyle Evans 	    EPERM);
20393900fe7SKyle Evans 	ATF_REQUIRE(memfd_truncate_test(4, 4, F_SEAL_GROW | F_SEAL_SHRINK) ==
20493900fe7SKyle Evans 	    0);
20593900fe7SKyle Evans }
20693900fe7SKyle Evans 
20793900fe7SKyle Evans ATF_TC_WITHOUT_HEAD(get_seals);
ATF_TC_BODY(get_seals,tc)20893900fe7SKyle Evans ATF_TC_BODY(get_seals, tc)
20993900fe7SKyle Evans {
21093900fe7SKyle Evans 	int fd;
21193900fe7SKyle Evans 	int seals;
21293900fe7SKyle Evans 
21393900fe7SKyle Evans 	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
21493900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == 0);
21593900fe7SKyle Evans 
21693900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW) == 0);
21793900fe7SKyle Evans 	seals = fcntl(fd, F_GET_SEALS);
21893900fe7SKyle Evans 	ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW));
21993900fe7SKyle Evans 
22093900fe7SKyle Evans 	close(fd);
22193900fe7SKyle Evans }
22293900fe7SKyle Evans 
22393900fe7SKyle Evans ATF_TC_WITHOUT_HEAD(dup_seals);
ATF_TC_BODY(dup_seals,tc)22493900fe7SKyle Evans ATF_TC_BODY(dup_seals, tc)
22593900fe7SKyle Evans {
22693900fe7SKyle Evans 	char buf[8];
22793900fe7SKyle Evans 	int fd, fdx;
22893900fe7SKyle Evans 	int seals;
22993900fe7SKyle Evans 
23093900fe7SKyle Evans 	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
23193900fe7SKyle Evans 	ATF_REQUIRE((fdx = dup(fd)) != -1);
23293900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == 0);
23393900fe7SKyle Evans 
23493900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW) == 0);
23593900fe7SKyle Evans 	seals = fcntl(fd, F_GET_SEALS);
23693900fe7SKyle Evans 	ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW));
23793900fe7SKyle Evans 
23893900fe7SKyle Evans 	seals = fcntl(fdx, F_GET_SEALS);
23993900fe7SKyle Evans 	ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW));
24093900fe7SKyle Evans 
24193900fe7SKyle Evans 	/* Make sure the seal's actually being applied at the inode level */
24293900fe7SKyle Evans 	ATF_REQUIRE(write(fdx, buf, sizeof(buf)) == -1);
24393900fe7SKyle Evans 	ATF_REQUIRE(errno == EPERM);
24493900fe7SKyle Evans 
24593900fe7SKyle Evans 	ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED,
24693900fe7SKyle Evans 	    fdx, 0) == MAP_FAILED);
24793900fe7SKyle Evans 	ATF_REQUIRE(errno == EACCES);
24893900fe7SKyle Evans 
24993900fe7SKyle Evans 	close(fd);
25093900fe7SKyle Evans 	close(fdx);
25193900fe7SKyle Evans }
25293900fe7SKyle Evans 
25393900fe7SKyle Evans ATF_TC_WITHOUT_HEAD(immutable_seals);
ATF_TC_BODY(immutable_seals,tc)25493900fe7SKyle Evans ATF_TC_BODY(immutable_seals, tc)
25593900fe7SKyle Evans {
25693900fe7SKyle Evans 	int fd;
25793900fe7SKyle Evans 
25893900fe7SKyle Evans 	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
25993900fe7SKyle Evans 
26093900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_SEAL) == 0);
26193900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW) == -1);
26293900fe7SKyle Evans 	ATF_REQUIRE_MSG(errno == EPERM,
26393900fe7SKyle Evans 	    "Added unique grow seal after restricting seals");
26493900fe7SKyle Evans 
26593900fe7SKyle Evans 	close(fd);
26693900fe7SKyle Evans 
26793900fe7SKyle Evans 	/*
26893900fe7SKyle Evans 	 * Also check that adding a seal that already exists really doesn't
26993900fe7SKyle Evans 	 * do anything once we're sealed.
27093900fe7SKyle Evans 	 */
27193900fe7SKyle Evans 	ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
27293900fe7SKyle Evans 
27393900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SEAL) == 0);
27493900fe7SKyle Evans 	ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW) == -1);
27593900fe7SKyle Evans 	ATF_REQUIRE_MSG(errno == EPERM,
27693900fe7SKyle Evans 	    "Added duplicate grow seal after restricting seals");
27793900fe7SKyle Evans 	close(fd);
27893900fe7SKyle Evans }
27993900fe7SKyle Evans 
ATF_TP_ADD_TCS(tp)28093900fe7SKyle Evans ATF_TP_ADD_TCS(tp)
28193900fe7SKyle Evans {
28293900fe7SKyle Evans 
28393900fe7SKyle Evans 	ATF_TP_ADD_TC(tp, basic);
28493900fe7SKyle Evans 	ATF_TP_ADD_TC(tp, cloexec);
28593900fe7SKyle Evans 	ATF_TP_ADD_TC(tp, disallowed_sealing);
28693900fe7SKyle Evans 	ATF_TP_ADD_TC(tp, write_seal);
28793900fe7SKyle Evans 	ATF_TP_ADD_TC(tp, mmap_write_seal);
28893900fe7SKyle Evans 	ATF_TP_ADD_TC(tp, truncate_seals);
28993900fe7SKyle Evans 	ATF_TP_ADD_TC(tp, get_seals);
29093900fe7SKyle Evans 	ATF_TP_ADD_TC(tp, dup_seals);
29193900fe7SKyle Evans 	ATF_TP_ADD_TC(tp, immutable_seals);
29293900fe7SKyle Evans 	return (atf_no_error());
29393900fe7SKyle Evans }
294