mirror of https://github.com/torvalds/linux.git
Landlock fix for v6.10-rc2
-----BEGIN PGP SIGNATURE----- iIYEABYKAC4WIQSVyBthFV4iTW/VU1/l49DojIL20gUCZlntgBAcbWljQGRpZ2lr b2QubmV0AAoJEOXj0OiMgvbShXAA/3ecCnZWTkgHKSKQiS+t1nUkQmAgZ1Zzmm6v UaJDCOBnAP9nAEfgEgMHZO1LVL38N6EWzexjnglFkZFHpT8gFwKtCw== =R5nh -----END PGP SIGNATURE----- Merge tag 'landlock-6.10-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux 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
This commit is contained in:
commit
d9aab0b1c9
|
|
@ -1110,6 +1110,7 @@ static int current_check_refer_path(struct dentry *const old_dentry,
|
||||||
bool allow_parent1, allow_parent2;
|
bool allow_parent1, allow_parent2;
|
||||||
access_mask_t access_request_parent1, access_request_parent2;
|
access_mask_t access_request_parent1, access_request_parent2;
|
||||||
struct path mnt_dir;
|
struct path mnt_dir;
|
||||||
|
struct dentry *old_parent;
|
||||||
layer_mask_t layer_masks_parent1[LANDLOCK_NUM_ACCESS_FS] = {},
|
layer_mask_t layer_masks_parent1[LANDLOCK_NUM_ACCESS_FS] = {},
|
||||||
layer_masks_parent2[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.mnt = new_dir->mnt;
|
||||||
mnt_dir.dentry = new_dir->mnt->mnt_root;
|
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 */
|
/* new_dir->dentry is equal to new_dentry->d_parent */
|
||||||
allow_parent1 = collect_domain_accesses(dom, mnt_dir.dentry,
|
allow_parent1 = collect_domain_accesses(dom, mnt_dir.dentry, old_parent,
|
||||||
old_dentry->d_parent,
|
|
||||||
&layer_masks_parent1);
|
&layer_masks_parent1);
|
||||||
allow_parent2 = collect_domain_accesses(
|
allow_parent2 = collect_domain_accesses(
|
||||||
dom, mnt_dir.dentry, new_dir->dentry, &layer_masks_parent2);
|
dom, mnt_dir.dentry, new_dir->dentry, &layer_masks_parent2);
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@
|
||||||
* See https://sourceware.org/glibc/wiki/Synchronizing_Headers.
|
* See https://sourceware.org/glibc/wiki/Synchronizing_Headers.
|
||||||
*/
|
*/
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
|
@ -47,6 +48,13 @@ int renameat2(int olddirfd, const char *oldpath, int newdirfd,
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
#ifndef RENAME_EXCHANGE
|
||||||
#define RENAME_EXCHANGE (1 << 1)
|
#define RENAME_EXCHANGE (1 << 1)
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -2400,6 +2408,43 @@ TEST_F_FORK(layout1, refer_denied_by_default4)
|
||||||
layer_dir_s1d1_refer);
|
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)
|
TEST_F_FORK(layout1, reparent_link)
|
||||||
{
|
{
|
||||||
const struct rule layer1[] = {
|
const struct rule layer1[] = {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue