Unverified Commit 88da52cc authored by Mickaël Salaün's avatar Mickaël Salaün
Browse files

landlock: Fix d_parent walk



The WARN_ON_ONCE() in collect_domain_accesses() can be triggered when
trying to link a root mount point.  This cannot work in practice because
this directory is mounted, but the VFS check is done after the call to
security_path_link().

Do not use source directory's d_parent when the source directory is the
mount point.

Cc: Günther Noack <gnoack@google.com>
Cc: Paul Moore <paul@paul-moore.com>
Cc: stable@vger.kernel.org
Reported-by: default avatar <syzbot+bf4903dc7e12b18ebc87@syzkaller.appspotmail.com>
Fixes: b91c3e4e ("landlock: Add support for file reparenting with LANDLOCK_ACCESS_FS_REFER")
Closes: https://lore.kernel.org/r/000000000000553d3f0618198200@google.com
Link: https://lore.kernel.org/r/20240516181935.1645983-2-mic@digikod.net


[mic: Fix commit message]
Signed-off-by: default avatarMickaël Salaün <mic@digikod.net>
parent 1613e604
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);