Commit d9aab0b1 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull landlock fix from Mickaël Salaün:
 "This fixes a wrong path walk triggered by syzkaller"

* tag 'landlock-6.10-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux:
  selftests/landlock: Add layout1.refer_mount_root
  landlock: Fix d_parent walk
parents cc8ed4d0 0055f53a
Loading
Loading
Loading
Loading
+11 −2
Original line number Diff line number Diff line
@@ -1110,6 +1110,7 @@ static int current_check_refer_path(struct dentry *const old_dentry,
	bool allow_parent1, allow_parent2;
	access_mask_t access_request_parent1, access_request_parent2;
	struct path mnt_dir;
	struct dentry *old_parent;
	layer_mask_t layer_masks_parent1[LANDLOCK_NUM_ACCESS_FS] = {},
		     layer_masks_parent2[LANDLOCK_NUM_ACCESS_FS] = {};

@@ -1157,9 +1158,17 @@ static int current_check_refer_path(struct dentry *const old_dentry,
	mnt_dir.mnt = new_dir->mnt;
	mnt_dir.dentry = new_dir->mnt->mnt_root;

	/*
	 * old_dentry may be the root of the common mount point and
	 * !IS_ROOT(old_dentry) at the same time (e.g. with open_tree() and
	 * OPEN_TREE_CLONE).  We do not need to call dget(old_parent) because
	 * we keep a reference to old_dentry.
	 */
	old_parent = (old_dentry == mnt_dir.dentry) ? old_dentry :
						      old_dentry->d_parent;

	/* new_dir->dentry is equal to new_dentry->d_parent */
	allow_parent1 = collect_domain_accesses(dom, mnt_dir.dentry,
						old_dentry->d_parent,
	allow_parent1 = collect_domain_accesses(dom, mnt_dir.dentry, old_parent,
						&layer_masks_parent1);
	allow_parent2 = collect_domain_accesses(
		dom, mnt_dir.dentry, new_dir->dentry, &layer_masks_parent2);
+45 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@
 * See https://sourceware.org/glibc/wiki/Synchronizing_Headers.
 */
#include <linux/fs.h>
#include <linux/mount.h>

#include "common.h"

@@ -47,6 +48,13 @@ int renameat2(int olddirfd, const char *oldpath, int newdirfd,
}
#endif

#ifndef open_tree
int open_tree(int dfd, const char *filename, unsigned int flags)
{
	return syscall(__NR_open_tree, dfd, filename, flags);
}
#endif

#ifndef RENAME_EXCHANGE
#define RENAME_EXCHANGE (1 << 1)
#endif
@@ -2400,6 +2408,43 @@ TEST_F_FORK(layout1, refer_denied_by_default4)
				layer_dir_s1d1_refer);
}

/*
 * Tests walking through a denied root mount.
 */
TEST_F_FORK(layout1, refer_mount_root_deny)
{
	const struct landlock_ruleset_attr ruleset_attr = {
		.handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_DIR,
	};
	int root_fd, ruleset_fd;

	/* Creates a mount object from a non-mount point. */
	set_cap(_metadata, CAP_SYS_ADMIN);
	root_fd =
		open_tree(AT_FDCWD, dir_s1d1,
			  AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
	clear_cap(_metadata, CAP_SYS_ADMIN);
	ASSERT_LE(0, root_fd);

	ruleset_fd =
		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
	ASSERT_LE(0, ruleset_fd);

	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
	EXPECT_EQ(0, close(ruleset_fd));

	/* Link denied by Landlock: EACCES. */
	EXPECT_EQ(-1, linkat(root_fd, ".", root_fd, "does_not_exist", 0));
	EXPECT_EQ(EACCES, errno);

	/* renameat2() always returns EBUSY. */
	EXPECT_EQ(-1, renameat2(root_fd, ".", root_fd, "does_not_exist", 0));
	EXPECT_EQ(EBUSY, errno);

	EXPECT_EQ(0, close(root_fd));
}

TEST_F_FORK(layout1, reparent_link)
{
	const struct rule layer1[] = {