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