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