Changeset View
Changeset View
Standalone View
Standalone View
tests/sys/kern/memfd_test.c
- This file was added.
/*- | |||||
* 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) == -1); | |||||
ATF_REQUIRE(errno == EINVAL); | |||||
ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1); | |||||
ATF_REQUIRE(errno == EINVAL); | |||||
close(fd); | |||||
} | |||||
ATF_TC_WITHOUT_HEAD(write_seal); | |||||
ATF_TC_BODY(write_seal, tc) | |||||
{ | |||||
int fd; | |||||
char buf[8]; | |||||
ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1); | |||||
ATF_REQUIRE(ftruncate(fd, sizeof(buf)) == 0); | |||||
/* Write once, then we'll seal it and try again */ | |||||
ATF_REQUIRE(write(fd, buf, sizeof(buf)) == sizeof(buf)); | |||||
ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0); | |||||
ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == 0); | |||||
ATF_REQUIRE(write(fd, buf, sizeof(buf)) == -1); | |||||
ATF_REQUIRE(errno == EPERM); | |||||
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); | |||||
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, 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()); | |||||
} |