mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 23:59:33 -04:00
selftests/landlock: Add tests for whiteout object creation
Add tests to check that whiteout object creation is guarded by LANDLOCK_ACCESS_FS_MAKE_REG, in the cases where these are created from userspace: * Conventional creation with mknod() * Linking or renaming an existing whiteout object * renameat2() with RENAME_WHITEOUT, which creates a new whiteout object in the source location * renameat2() with RENAME_EXCHANGE, with one of the renamed objects being a whiteout object Signed-off-by: Günther Noack <gnoack@google.com> Link: https://patch.msgid.link/20260813093157.1436894-4-gnoack@google.com [mic: Update commit message as requested] Signed-off-by: Mickaël Salaün <mic@digikod.net>
This commit is contained in:
committed by
Mickaël Salaün
parent
672fa082d4
commit
ee890889b3
@@ -2247,6 +2247,170 @@ TEST_F_FORK(layout1, rename_file)
|
||||
RENAME_EXCHANGE));
|
||||
}
|
||||
|
||||
TEST_F_FORK(layout1, rename_whiteout_denied)
|
||||
{
|
||||
/* The affected file is a FIFO. */
|
||||
ASSERT_EQ(0, unlink(file1_s3d3));
|
||||
ASSERT_EQ(0, mknod(file1_s3d3, S_IFIFO | 0600, 0));
|
||||
|
||||
/* Deny MAKE_REG, but allow MAKE_FIFO. */
|
||||
enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, NULL);
|
||||
|
||||
/*
|
||||
* Try to rename a file with RENAME_WHITEOUT.
|
||||
* file1_s3d3 is in dir_s3d2 (tmpfs), so it supports RENAME_WHITEOUT.
|
||||
* Denied, because whiteout creation is guarded with MAKE_REG.
|
||||
*/
|
||||
EXPECT_EQ(-1, renameat2(AT_FDCWD, file1_s3d3, AT_FDCWD,
|
||||
TMP_DIR "/s3d1/s3d2/s3d3/f2", RENAME_WHITEOUT));
|
||||
EXPECT_EQ(EACCES, errno);
|
||||
}
|
||||
|
||||
static bool is_whiteout(const char *const path)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(path, &st) == -1)
|
||||
return false;
|
||||
|
||||
return S_ISCHR(st.st_mode) && st.st_rdev == makedev(0, 0);
|
||||
}
|
||||
|
||||
static bool is_fifo(const char *const path)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
return stat(path, &st) == 0 && S_ISFIFO(st.st_mode);
|
||||
}
|
||||
|
||||
TEST_F_FORK(layout1, rename_whiteout_allowed)
|
||||
{
|
||||
const struct rule rules[] = {
|
||||
{
|
||||
.path = dir_s3d3,
|
||||
.access = LANDLOCK_ACCESS_FS_MAKE_REG,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
/* The affected file is a FIFO. */
|
||||
ASSERT_EQ(0, unlink(file1_s3d3));
|
||||
ASSERT_EQ(0, mknod(file1_s3d3, S_IFIFO | 0600, 0));
|
||||
|
||||
/* Allow MAKE_REG below dir_s3d3. */
|
||||
enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, rules);
|
||||
|
||||
/*
|
||||
* Rename a file with RENAME_WHITEOUT within the same directory.
|
||||
* Allowed, because MAKE_REG is granted for the whiteout object which
|
||||
* gets created in the source location.
|
||||
*/
|
||||
EXPECT_EQ(0, renameat2(AT_FDCWD, file1_s3d3, AT_FDCWD,
|
||||
TMP_DIR "/s3d1/s3d2/s3d3/f2", RENAME_WHITEOUT));
|
||||
|
||||
/* A whiteout object took the place of the moved FIFO. */
|
||||
EXPECT_TRUE(is_whiteout(file1_s3d3));
|
||||
EXPECT_TRUE(is_fifo(TMP_DIR "/s3d1/s3d2/s3d3/f2"));
|
||||
}
|
||||
|
||||
TEST_F_FORK(layout1, rename_whiteout_reparenting)
|
||||
{
|
||||
const struct rule rules[] = {
|
||||
{
|
||||
.path = dir_s3d2,
|
||||
.access = LANDLOCK_ACCESS_FS_REFER,
|
||||
},
|
||||
{
|
||||
.path = dir_s3d3,
|
||||
.access = LANDLOCK_ACCESS_FS_MAKE_REG,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
/* The moved files are FIFOs. */
|
||||
ASSERT_EQ(0, unlink(file1_s3d3));
|
||||
ASSERT_EQ(0, mknod(file1_s3d3, S_IFIFO | 0600, 0));
|
||||
ASSERT_EQ(0, unlink(file1_s3d4));
|
||||
ASSERT_EQ(0, mknod(file1_s3d4, S_IFIFO | 0600, 0));
|
||||
|
||||
/* Allow REFER below dir_s3d2, but MAKE_REG only below dir_s3d3. */
|
||||
enforce_fs(_metadata,
|
||||
LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER,
|
||||
rules);
|
||||
|
||||
/*
|
||||
* The whiteout object is created in the source directory: Moving the
|
||||
* FIFO out of dir_s3d4 is denied because MAKE_REG is not granted
|
||||
* there, even though it is granted in the destination directory
|
||||
* dir_s3d3.
|
||||
*/
|
||||
EXPECT_EQ(-1, renameat2(AT_FDCWD, file1_s3d4, AT_FDCWD,
|
||||
TMP_DIR "/s3d1/s3d2/s3d3/f2", RENAME_WHITEOUT));
|
||||
EXPECT_EQ(EACCES, errno);
|
||||
|
||||
/*
|
||||
* Moving the FIFO out of dir_s3d3 is allowed, because MAKE_REG is
|
||||
* granted there for the created whiteout object.
|
||||
*/
|
||||
EXPECT_EQ(0, renameat2(AT_FDCWD, file1_s3d3, AT_FDCWD,
|
||||
TMP_DIR "/s3d1/s3d2/s3d4/f2", RENAME_WHITEOUT));
|
||||
|
||||
/* A whiteout object took the place of the moved FIFO. */
|
||||
EXPECT_TRUE(is_whiteout(file1_s3d3));
|
||||
EXPECT_TRUE(is_fifo(TMP_DIR "/s3d1/s3d2/s3d4/f2"));
|
||||
}
|
||||
|
||||
TEST_F_FORK(layout1, rename_whiteout_exchange)
|
||||
{
|
||||
const char *const whiteout_s3d3 = TMP_DIR "/s3d1/s3d2/s3d3/f2";
|
||||
const struct rule rules[] = {
|
||||
{
|
||||
.path = dir_s3d2,
|
||||
.access = LANDLOCK_ACCESS_FS_REFER,
|
||||
},
|
||||
{
|
||||
.path = dir_s3d3,
|
||||
.access = LANDLOCK_ACCESS_FS_MAKE_REG,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
/* The exchanged files are FIFOs and an existing whiteout object. */
|
||||
ASSERT_EQ(0, unlink(file1_s3d3));
|
||||
ASSERT_EQ(0, mknod(file1_s3d3, S_IFIFO | 0600, 0));
|
||||
ASSERT_EQ(0, mknod(whiteout_s3d3, S_IFCHR | 0600, makedev(0, 0)));
|
||||
ASSERT_EQ(0, unlink(file1_s3d4));
|
||||
ASSERT_EQ(0, mknod(file1_s3d4, S_IFIFO | 0600, 0));
|
||||
|
||||
/* Allow REFER below dir_s3d2, but MAKE_REG only below dir_s3d3. */
|
||||
enforce_fs(_metadata,
|
||||
LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER,
|
||||
rules);
|
||||
|
||||
/*
|
||||
* With RENAME_EXCHANGE, the whiteout object moves into the source
|
||||
* directory of the rename: Exchanging the FIFO in dir_s3d4 with the
|
||||
* whiteout object is denied because MAKE_REG is not granted in
|
||||
* dir_s3d4, even though it is granted in the whiteout object's own
|
||||
* directory dir_s3d3.
|
||||
*/
|
||||
EXPECT_EQ(-1, renameat2(AT_FDCWD, file1_s3d4, AT_FDCWD, whiteout_s3d3,
|
||||
RENAME_EXCHANGE));
|
||||
EXPECT_EQ(EACCES, errno);
|
||||
|
||||
/*
|
||||
* Exchanging the FIFO in dir_s3d3 with the whiteout object is
|
||||
* allowed, because MAKE_REG is granted in the directory into which
|
||||
* the whiteout object moves.
|
||||
*/
|
||||
EXPECT_EQ(0, renameat2(AT_FDCWD, file1_s3d3, AT_FDCWD, whiteout_s3d3,
|
||||
RENAME_EXCHANGE));
|
||||
|
||||
/* The FIFO and the whiteout object swapped places. */
|
||||
EXPECT_TRUE(is_whiteout(file1_s3d3));
|
||||
EXPECT_TRUE(is_fifo(whiteout_s3d3));
|
||||
}
|
||||
|
||||
TEST_F_FORK(layout1, rename_dir)
|
||||
{
|
||||
const struct rule rules[] = {
|
||||
@@ -3270,6 +3434,18 @@ TEST_F_FORK(layout1, make_char)
|
||||
makedev(1, 3));
|
||||
}
|
||||
|
||||
TEST_F_FORK(layout1, make_whiteout)
|
||||
{
|
||||
/*
|
||||
* Creates a whiteout object (creation guarded by MAKE_REG).
|
||||
*
|
||||
* Contrary to the other character devices, this does not require
|
||||
* CAP_MKNOD, cf. vfs_mknod().
|
||||
*/
|
||||
test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFCHR,
|
||||
makedev(0, 0));
|
||||
}
|
||||
|
||||
TEST_F_FORK(layout1, make_block)
|
||||
{
|
||||
/* Creates a /dev/loop0 device. */
|
||||
|
||||
Reference in New Issue
Block a user