Skip to content

Commit 3ecf19e

Browse files
gnoackl0kod
authored andcommitted
selftests/landlock: Test IOCTL support
Exercises Landlock's IOCTL feature in different combinations of handling and permitting the LANDLOCK_ACCESS_FS_IOCTL_DEV right, and in different combinations of using files and directories. Signed-off-by: Günther Noack <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mickaël Salaün <[email protected]>
1 parent b25f741 commit 3ecf19e

File tree

1 file changed

+189
-3
lines changed

1 file changed

+189
-3
lines changed

tools/testing/selftests/landlock/fs_test.c

Lines changed: 189 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
#define _GNU_SOURCE
11+
#include <asm/termbits.h>
1112
#include <fcntl.h>
1213
#include <libgen.h>
1314
#include <linux/landlock.h>
@@ -16,6 +17,7 @@
1617
#include <stdio.h>
1718
#include <string.h>
1819
#include <sys/capability.h>
20+
#include <sys/ioctl.h>
1921
#include <sys/mount.h>
2022
#include <sys/prctl.h>
2123
#include <sys/sendfile.h>
@@ -24,6 +26,12 @@
2426
#include <sys/vfs.h>
2527
#include <unistd.h>
2628

29+
/*
30+
* Intentionally included last to work around header conflict.
31+
* See https://sourceware.org/glibc/wiki/Synchronizing_Headers.
32+
*/
33+
#include <linux/fs.h>
34+
2735
#include "common.h"
2836

2937
#ifndef renameat2
@@ -744,6 +752,9 @@ static int create_ruleset(struct __test_metadata *const _metadata,
744752
}
745753

746754
for (i = 0; rules[i].path; i++) {
755+
if (!rules[i].access)
756+
continue;
757+
747758
add_path_beneath(_metadata, ruleset_fd, rules[i].access,
748759
rules[i].path);
749760
}
@@ -3452,7 +3463,7 @@ TEST_F_FORK(layout1, truncate_unhandled)
34523463
LANDLOCK_ACCESS_FS_WRITE_FILE;
34533464
int ruleset_fd;
34543465

3455-
/* Enable Landlock. */
3466+
/* Enables Landlock. */
34563467
ruleset_fd = create_ruleset(_metadata, handled, rules);
34573468

34583469
ASSERT_LE(0, ruleset_fd);
@@ -3535,7 +3546,7 @@ TEST_F_FORK(layout1, truncate)
35353546
LANDLOCK_ACCESS_FS_TRUNCATE;
35363547
int ruleset_fd;
35373548

3538-
/* Enable Landlock. */
3549+
/* Enables Landlock. */
35393550
ruleset_fd = create_ruleset(_metadata, handled, rules);
35403551

35413552
ASSERT_LE(0, ruleset_fd);
@@ -3761,7 +3772,7 @@ TEST_F_FORK(ftruncate, open_and_ftruncate)
37613772
};
37623773
int fd, ruleset_fd;
37633774

3764-
/* Enable Landlock. */
3775+
/* Enables Landlock. */
37653776
ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
37663777
ASSERT_LE(0, ruleset_fd);
37673778
enforce_ruleset(_metadata, ruleset_fd);
@@ -3854,6 +3865,181 @@ TEST(memfd_ftruncate)
38543865
ASSERT_EQ(0, close(fd));
38553866
}
38563867

3868+
static int test_fionread_ioctl(int fd)
3869+
{
3870+
size_t sz = 0;
3871+
3872+
if (ioctl(fd, FIONREAD, &sz) < 0 && errno == EACCES)
3873+
return errno;
3874+
return 0;
3875+
}
3876+
3877+
/* clang-format off */
3878+
FIXTURE(ioctl) {};
3879+
3880+
FIXTURE_SETUP(ioctl) {};
3881+
3882+
FIXTURE_TEARDOWN(ioctl) {};
3883+
/* clang-format on */
3884+
3885+
FIXTURE_VARIANT(ioctl)
3886+
{
3887+
const __u64 handled;
3888+
const __u64 allowed;
3889+
const mode_t open_mode;
3890+
/*
3891+
* FIONREAD is used as a characteristic device-specific IOCTL command.
3892+
* It is implemented in fs/ioctl.c for regular files,
3893+
* but we do not blanket-permit it for devices.
3894+
*/
3895+
const int expected_fionread_result;
3896+
};
3897+
3898+
/* clang-format off */
3899+
FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_none) {
3900+
/* clang-format on */
3901+
.handled = LANDLOCK_ACCESS_FS_IOCTL_DEV,
3902+
.allowed = 0,
3903+
.open_mode = O_RDWR,
3904+
.expected_fionread_result = EACCES,
3905+
};
3906+
3907+
/* clang-format off */
3908+
FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_i) {
3909+
/* clang-format on */
3910+
.handled = LANDLOCK_ACCESS_FS_IOCTL_DEV,
3911+
.allowed = LANDLOCK_ACCESS_FS_IOCTL_DEV,
3912+
.open_mode = O_RDWR,
3913+
.expected_fionread_result = 0,
3914+
};
3915+
3916+
/* clang-format off */
3917+
FIXTURE_VARIANT_ADD(ioctl, unhandled) {
3918+
/* clang-format on */
3919+
.handled = LANDLOCK_ACCESS_FS_EXECUTE,
3920+
.allowed = LANDLOCK_ACCESS_FS_EXECUTE,
3921+
.open_mode = O_RDWR,
3922+
.expected_fionread_result = 0,
3923+
};
3924+
3925+
TEST_F_FORK(ioctl, handle_dir_access_file)
3926+
{
3927+
const int flag = 0;
3928+
const struct rule rules[] = {
3929+
{
3930+
.path = "/dev",
3931+
.access = variant->allowed,
3932+
},
3933+
{},
3934+
};
3935+
int file_fd, ruleset_fd;
3936+
3937+
/* Enables Landlock. */
3938+
ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
3939+
ASSERT_LE(0, ruleset_fd);
3940+
enforce_ruleset(_metadata, ruleset_fd);
3941+
ASSERT_EQ(0, close(ruleset_fd));
3942+
3943+
file_fd = open("/dev/zero", variant->open_mode);
3944+
ASSERT_LE(0, file_fd);
3945+
3946+
/* Checks that IOCTL commands return the expected errors. */
3947+
EXPECT_EQ(variant->expected_fionread_result,
3948+
test_fionread_ioctl(file_fd));
3949+
3950+
/* Checks that unrestrictable commands are unrestricted. */
3951+
EXPECT_EQ(0, ioctl(file_fd, FIOCLEX));
3952+
EXPECT_EQ(0, ioctl(file_fd, FIONCLEX));
3953+
EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag));
3954+
EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag));
3955+
EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag));
3956+
3957+
ASSERT_EQ(0, close(file_fd));
3958+
}
3959+
3960+
TEST_F_FORK(ioctl, handle_dir_access_dir)
3961+
{
3962+
const int flag = 0;
3963+
const struct rule rules[] = {
3964+
{
3965+
.path = "/dev",
3966+
.access = variant->allowed,
3967+
},
3968+
{},
3969+
};
3970+
int dir_fd, ruleset_fd;
3971+
3972+
/* Enables Landlock. */
3973+
ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
3974+
ASSERT_LE(0, ruleset_fd);
3975+
enforce_ruleset(_metadata, ruleset_fd);
3976+
ASSERT_EQ(0, close(ruleset_fd));
3977+
3978+
/*
3979+
* Ignore variant->open_mode for this test, as we intend to open a
3980+
* directory. If the directory can not be opened, the variant is
3981+
* infeasible to test with an opened directory.
3982+
*/
3983+
dir_fd = open("/dev", O_RDONLY);
3984+
if (dir_fd < 0)
3985+
return;
3986+
3987+
/*
3988+
* Checks that IOCTL commands return the expected errors.
3989+
* We do not use the expected values from the fixture here.
3990+
*
3991+
* When using IOCTL on a directory, no Landlock restrictions apply.
3992+
*/
3993+
EXPECT_EQ(0, test_fionread_ioctl(dir_fd));
3994+
3995+
/* Checks that unrestrictable commands are unrestricted. */
3996+
EXPECT_EQ(0, ioctl(dir_fd, FIOCLEX));
3997+
EXPECT_EQ(0, ioctl(dir_fd, FIONCLEX));
3998+
EXPECT_EQ(0, ioctl(dir_fd, FIONBIO, &flag));
3999+
EXPECT_EQ(0, ioctl(dir_fd, FIOASYNC, &flag));
4000+
EXPECT_EQ(0, ioctl(dir_fd, FIGETBSZ, &flag));
4001+
4002+
ASSERT_EQ(0, close(dir_fd));
4003+
}
4004+
4005+
TEST_F_FORK(ioctl, handle_file_access_file)
4006+
{
4007+
const int flag = 0;
4008+
const struct rule rules[] = {
4009+
{
4010+
.path = "/dev/zero",
4011+
.access = variant->allowed,
4012+
},
4013+
{},
4014+
};
4015+
int file_fd, ruleset_fd;
4016+
4017+
/* Enables Landlock. */
4018+
ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
4019+
ASSERT_LE(0, ruleset_fd);
4020+
enforce_ruleset(_metadata, ruleset_fd);
4021+
ASSERT_EQ(0, close(ruleset_fd));
4022+
4023+
file_fd = open("/dev/zero", variant->open_mode);
4024+
ASSERT_LE(0, file_fd)
4025+
{
4026+
TH_LOG("Failed to open /dev/zero: %s", strerror(errno));
4027+
}
4028+
4029+
/* Checks that IOCTL commands return the expected errors. */
4030+
EXPECT_EQ(variant->expected_fionread_result,
4031+
test_fionread_ioctl(file_fd));
4032+
4033+
/* Checks that unrestrictable commands are unrestricted. */
4034+
EXPECT_EQ(0, ioctl(file_fd, FIOCLEX));
4035+
EXPECT_EQ(0, ioctl(file_fd, FIONCLEX));
4036+
EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag));
4037+
EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag));
4038+
EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag));
4039+
4040+
ASSERT_EQ(0, close(file_fd));
4041+
}
4042+
38574043
/* clang-format off */
38584044
FIXTURE(layout1_bind) {};
38594045
/* clang-format on */

0 commit comments

Comments
 (0)