Skip to content

Commit bce605e

Browse files
gnoackl0kod
authored andcommitted
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>
1 parent f83d51a commit bce605e

File tree

1 file changed

+114
-0
lines changed

1 file changed

+114
-0
lines changed

tools/testing/selftests/landlock/fs_test.c

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <asm/termbits.h>
1212
#include <fcntl.h>
1313
#include <libgen.h>
14+
#include <linux/fiemap.h>
1415
#include <linux/landlock.h>
1516
#include <linux/magic.h>
1617
#include <sched.h>
@@ -3945,6 +3946,119 @@ TEST_F_FORK(layout1, o_path_ftruncate_and_ioctl)
39453946
ASSERT_EQ(0, close(fd));
39463947
}
39473948

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+
39484062
/*
39494063
* Named pipes are not governed by the LANDLOCK_ACCESS_FS_IOCTL_DEV right,
39504064
* because they are not character or block devices.

0 commit comments

Comments
 (0)