mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 06:01:57 +00:00
selftests/landlock: Exhaustive test for the IOCTL allow-list
This test checks all IOCTL commands implemented in do_vfs_ioctl(). Test coverage for security/landlock is 90.9% of 722 lines according to gcc/gcov-13. Suggested-by: Mickaël Salaün <mic@digikod.net> Signed-off-by: Günther Noack <gnoack@google.com> Link: https://lore.kernel.org/r/20240419161122.2023765-8-gnoack@google.com [mic: Add test coverage] Signed-off-by: Mickaël Salaün <mic@digikod.net>
This commit is contained in:
parent
f83d51a5bd
commit
bce605e0cf
@ -11,6 +11,7 @@
|
||||
#include <asm/termbits.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <linux/fiemap.h>
|
||||
#include <linux/landlock.h>
|
||||
#include <linux/magic.h>
|
||||
#include <sched.h>
|
||||
@ -3945,6 +3946,119 @@ TEST_F_FORK(layout1, o_path_ftruncate_and_ioctl)
|
||||
ASSERT_EQ(0, close(fd));
|
||||
}
|
||||
|
||||
/*
|
||||
* ioctl_error - generically call the given ioctl with a pointer to a
|
||||
* sufficiently large zeroed-out memory region.
|
||||
*
|
||||
* Returns the IOCTLs error, or 0.
|
||||
*/
|
||||
static int ioctl_error(struct __test_metadata *const _metadata, int fd,
|
||||
unsigned int cmd)
|
||||
{
|
||||
char buf[128]; /* sufficiently large */
|
||||
int res, stdinbak_fd;
|
||||
|
||||
/*
|
||||
* Depending on the IOCTL command, parts of the zeroed-out buffer might
|
||||
* be interpreted as file descriptor numbers. We do not want to
|
||||
* accidentally operate on file descriptor 0 (stdin), so we temporarily
|
||||
* move stdin to a different FD and close FD 0 for the IOCTL call.
|
||||
*/
|
||||
stdinbak_fd = dup(0);
|
||||
ASSERT_LT(0, stdinbak_fd);
|
||||
ASSERT_EQ(0, close(0));
|
||||
|
||||
/* Invokes the IOCTL with a zeroed-out buffer. */
|
||||
bzero(&buf, sizeof(buf));
|
||||
res = ioctl(fd, cmd, &buf);
|
||||
|
||||
/* Restores the old FD 0 and closes the backup FD. */
|
||||
ASSERT_EQ(0, dup2(stdinbak_fd, 0));
|
||||
ASSERT_EQ(0, close(stdinbak_fd));
|
||||
|
||||
if (res < 0)
|
||||
return errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Define some linux/falloc.h IOCTL commands which are not available in uapi headers. */
|
||||
struct space_resv {
|
||||
__s16 l_type;
|
||||
__s16 l_whence;
|
||||
__s64 l_start;
|
||||
__s64 l_len; /* len == 0 means until end of file */
|
||||
__s32 l_sysid;
|
||||
__u32 l_pid;
|
||||
__s32 l_pad[4]; /* reserved area */
|
||||
};
|
||||
|
||||
#define FS_IOC_RESVSP _IOW('X', 40, struct space_resv)
|
||||
#define FS_IOC_UNRESVSP _IOW('X', 41, struct space_resv)
|
||||
#define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv)
|
||||
#define FS_IOC_UNRESVSP64 _IOW('X', 43, struct space_resv)
|
||||
#define FS_IOC_ZERO_RANGE _IOW('X', 57, struct space_resv)
|
||||
|
||||
/*
|
||||
* Tests a series of blanket-permitted and denied IOCTLs.
|
||||
*/
|
||||
TEST_F_FORK(layout1, blanket_permitted_ioctls)
|
||||
{
|
||||
const struct landlock_ruleset_attr attr = {
|
||||
.handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,
|
||||
};
|
||||
int ruleset_fd, fd;
|
||||
|
||||
/* Enables Landlock. */
|
||||
ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
|
||||
ASSERT_LE(0, ruleset_fd);
|
||||
enforce_ruleset(_metadata, ruleset_fd);
|
||||
ASSERT_EQ(0, close(ruleset_fd));
|
||||
|
||||
fd = open("/dev/null", O_RDWR | O_CLOEXEC);
|
||||
ASSERT_LE(0, fd);
|
||||
|
||||
/*
|
||||
* Checks permitted commands.
|
||||
* These ones may return errors, but should not be blocked by Landlock.
|
||||
*/
|
||||
EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOCLEX));
|
||||
EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONCLEX));
|
||||
EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONBIO));
|
||||
EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOASYNC));
|
||||
EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOQSIZE));
|
||||
EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIFREEZE));
|
||||
EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FITHAW));
|
||||
EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_FIEMAP));
|
||||
EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIGETBSZ));
|
||||
EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONE));
|
||||
EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONERANGE));
|
||||
EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIDEDUPERANGE));
|
||||
EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSUUID));
|
||||
EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSSYSFSPATH));
|
||||
|
||||
/*
|
||||
* Checks blocked commands.
|
||||
* A call to a blocked IOCTL command always returns EACCES.
|
||||
*/
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD));
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFLAGS));
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_SETFLAGS));
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSGETXATTR));
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSSETXATTR));
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIBMAP));
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP));
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP64));
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP));
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP64));
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_ZERO_RANGE));
|
||||
|
||||
/* Default case is also blocked. */
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, 0xc00ffeee));
|
||||
|
||||
ASSERT_EQ(0, close(fd));
|
||||
}
|
||||
|
||||
/*
|
||||
* Named pipes are not governed by the LANDLOCK_ACCESS_FS_IOCTL_DEV right,
|
||||
* because they are not character or block devices.
|
||||
|
Loading…
Reference in New Issue
Block a user