Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Günther Noack and committed by
Mickaël Salaün
bce605e0 f83d51a5

+114
+114
tools/testing/selftests/landlock/fs_test.c
··· 11 11 #include <asm/termbits.h> 12 12 #include <fcntl.h> 13 13 #include <libgen.h> 14 + #include <linux/fiemap.h> 14 15 #include <linux/landlock.h> 15 16 #include <linux/magic.h> 16 17 #include <sched.h> ··· 3942 3941 3943 3942 EXPECT_EQ(EBADF, test_ftruncate(fd)); 3944 3943 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd)); 3944 + 3945 + ASSERT_EQ(0, close(fd)); 3946 + } 3947 + 3948 + /* 3949 + * ioctl_error - generically call the given ioctl with a pointer to a 3950 + * sufficiently large zeroed-out memory region. 3951 + * 3952 + * Returns the IOCTLs error, or 0. 3953 + */ 3954 + static int ioctl_error(struct __test_metadata *const _metadata, int fd, 3955 + unsigned int cmd) 3956 + { 3957 + char buf[128]; /* sufficiently large */ 3958 + int res, stdinbak_fd; 3959 + 3960 + /* 3961 + * Depending on the IOCTL command, parts of the zeroed-out buffer might 3962 + * be interpreted as file descriptor numbers. We do not want to 3963 + * accidentally operate on file descriptor 0 (stdin), so we temporarily 3964 + * move stdin to a different FD and close FD 0 for the IOCTL call. 3965 + */ 3966 + stdinbak_fd = dup(0); 3967 + ASSERT_LT(0, stdinbak_fd); 3968 + ASSERT_EQ(0, close(0)); 3969 + 3970 + /* Invokes the IOCTL with a zeroed-out buffer. */ 3971 + bzero(&buf, sizeof(buf)); 3972 + res = ioctl(fd, cmd, &buf); 3973 + 3974 + /* Restores the old FD 0 and closes the backup FD. */ 3975 + ASSERT_EQ(0, dup2(stdinbak_fd, 0)); 3976 + ASSERT_EQ(0, close(stdinbak_fd)); 3977 + 3978 + if (res < 0) 3979 + return errno; 3980 + 3981 + return 0; 3982 + } 3983 + 3984 + /* Define some linux/falloc.h IOCTL commands which are not available in uapi headers. */ 3985 + struct space_resv { 3986 + __s16 l_type; 3987 + __s16 l_whence; 3988 + __s64 l_start; 3989 + __s64 l_len; /* len == 0 means until end of file */ 3990 + __s32 l_sysid; 3991 + __u32 l_pid; 3992 + __s32 l_pad[4]; /* reserved area */ 3993 + }; 3994 + 3995 + #define FS_IOC_RESVSP _IOW('X', 40, struct space_resv) 3996 + #define FS_IOC_UNRESVSP _IOW('X', 41, struct space_resv) 3997 + #define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv) 3998 + #define FS_IOC_UNRESVSP64 _IOW('X', 43, struct space_resv) 3999 + #define FS_IOC_ZERO_RANGE _IOW('X', 57, struct space_resv) 4000 + 4001 + /* 4002 + * Tests a series of blanket-permitted and denied IOCTLs. 4003 + */ 4004 + TEST_F_FORK(layout1, blanket_permitted_ioctls) 4005 + { 4006 + const struct landlock_ruleset_attr attr = { 4007 + .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4008 + }; 4009 + int ruleset_fd, fd; 4010 + 4011 + /* Enables Landlock. */ 4012 + ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 4013 + ASSERT_LE(0, ruleset_fd); 4014 + enforce_ruleset(_metadata, ruleset_fd); 4015 + ASSERT_EQ(0, close(ruleset_fd)); 4016 + 4017 + fd = open("/dev/null", O_RDWR | O_CLOEXEC); 4018 + ASSERT_LE(0, fd); 4019 + 4020 + /* 4021 + * Checks permitted commands. 4022 + * These ones may return errors, but should not be blocked by Landlock. 4023 + */ 4024 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOCLEX)); 4025 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONCLEX)); 4026 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONBIO)); 4027 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOASYNC)); 4028 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOQSIZE)); 4029 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIFREEZE)); 4030 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FITHAW)); 4031 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_FIEMAP)); 4032 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIGETBSZ)); 4033 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONE)); 4034 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONERANGE)); 4035 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIDEDUPERANGE)); 4036 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSUUID)); 4037 + EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSSYSFSPATH)); 4038 + 4039 + /* 4040 + * Checks blocked commands. 4041 + * A call to a blocked IOCTL command always returns EACCES. 4042 + */ 4043 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD)); 4044 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFLAGS)); 4045 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_SETFLAGS)); 4046 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSGETXATTR)); 4047 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSSETXATTR)); 4048 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIBMAP)); 4049 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP)); 4050 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP64)); 4051 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP)); 4052 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP64)); 4053 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_ZERO_RANGE)); 4054 + 4055 + /* Default case is also blocked. */ 4056 + EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, 0xc00ffeee)); 3945 4057 3946 4058 ASSERT_EQ(0, close(fd)); 3947 4059 }