Commit eee654ca authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull landlock updates from Mickaël Salaün:
 "This mainly fixes handling of disconnected directories and adds new
  tests"

* tag 'landlock-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux:
  selftests/landlock: Add disconnected leafs and branch test suites
  selftests/landlock: Add tests for access through disconnected paths
  landlock: Improve variable scope
  landlock: Fix handling of disconnected directories
  selftests/landlock: Fix makefile header list
  landlock: Make docs in cred.h and domain.h visible
  landlock: Minor comments improvements
parents 10003ff8 54f9baf5
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@ Landlock LSM: kernel documentation
==================================

:Author: Mickaël Salaün
:Date: March 2025
:Date: September 2025

Landlock's goal is to create scoped access-control (i.e. sandboxing).  To
harden a whole system, this feature should be available to any process,
@@ -110,6 +110,12 @@ Filesystem
.. kernel-doc:: security/landlock/fs.h
    :identifiers:

Process credential
------------------

.. kernel-doc:: security/landlock/cred.h
    :identifiers:

Ruleset and domain
------------------

@@ -128,6 +134,9 @@ makes the reasoning much easier and helps avoid pitfalls.
.. kernel-doc:: security/landlock/ruleset.h
    :identifiers:

.. kernel-doc:: security/landlock/domain.h
    :identifiers:

Additional documentation
========================

+16 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */

/**
 * DOC: erratum_3
 *
 * Erratum 3: Disconnected directory handling
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * This fix addresses an issue with disconnected directories that occur when a
 * directory is moved outside the scope of a bind mount.  The change ensures
 * that evaluated access rights include both those from the disconnected file
 * hierarchy down to its filesystem root and those from the related mount point
 * hierarchy.  This prevents access right widening through rename or link
 * actions.
 */
LANDLOCK_ERRATUM(3)
+32 −14
Original line number Diff line number Diff line
@@ -714,7 +714,8 @@ static void test_is_eacces_with_write(struct kunit *const test)
 * is_access_to_paths_allowed - Check accesses for requests with a common path
 *
 * @domain: Domain to check against.
 * @path: File hierarchy to walk through.
 * @path: File hierarchy to walk through.  For refer checks, this would be
 *     the common mountpoint.
 * @access_request_parent1: Accesses to check, once @layer_masks_parent1 is
 *     equal to @layer_masks_parent2 (if any).  This is tied to the unique
 *     requested path for most actions, or the source in case of a refer action
@@ -837,7 +838,6 @@ static bool is_access_to_paths_allowed(
	 * restriction.
	 */
	while (true) {
		struct dentry *parent_dentry;
		const struct landlock_rule *rule;

		/*
@@ -909,22 +909,34 @@ static bool is_access_to_paths_allowed(
				break;
			}
		}

		if (unlikely(IS_ROOT(walker_path.dentry))) {
			if (likely(walker_path.mnt->mnt_flags & MNT_INTERNAL)) {
				/*
			 * Stops at disconnected root directories.  Only allows
			 * access to internal filesystems (e.g. nsfs, which is
			 * reachable through /proc/<pid>/ns/<namespace>).
				 * Stops and allows access when reaching disconnected root
				 * directories that are part of internal filesystems (e.g. nsfs,
				 * which is reachable through /proc/<pid>/ns/<namespace>).
				 */
			if (walker_path.mnt->mnt_flags & MNT_INTERNAL) {
				allowed_parent1 = true;
				allowed_parent2 = true;
			}
				break;
			}
		parent_dentry = dget_parent(walker_path.dentry);

			/*
			 * We reached a disconnected root directory from a bind mount.
			 * Let's continue the walk with the mount point we missed.
			 */
			dput(walker_path.dentry);
			walker_path.dentry = walker_path.mnt->mnt_root;
			dget(walker_path.dentry);
		} else {
			struct dentry *const parent_dentry =
				dget_parent(walker_path.dentry);

			dput(walker_path.dentry);
			walker_path.dentry = parent_dentry;
		}
	}
	path_put(&walker_path);

	if (!allowed_parent1) {
@@ -1021,6 +1033,9 @@ static access_mask_t maybe_remove(const struct dentry *const dentry)
 * file.  While walking from @dir to @mnt_root, we record all the domain's
 * allowed accesses in @layer_masks_dom.
 *
 * Because of disconnected directories, this walk may not reach @mnt_dir.  In
 * this case, the walk will continue to @mnt_dir after this call.
 *
 * This is similar to is_access_to_paths_allowed() but much simpler because it
 * only handles walking on the same mount point and only checks one set of
 * accesses.
@@ -1062,8 +1077,11 @@ static bool collect_domain_accesses(
			break;
		}

		/* We should not reach a root other than @mnt_root. */
		if (dir == mnt_root || WARN_ON_ONCE(IS_ROOT(dir)))
		/*
		 * Stops at the mount point or the filesystem root for a disconnected
		 * directory.
		 */
		if (dir == mnt_root || unlikely(IS_ROOT(dir)))
			break;

		parent_dentry = dget_parent(dir);
+10 −2
Original line number Diff line number Diff line
@@ -83,6 +83,10 @@ static void build_check_rule(void)
		.num_layers = ~0,
	};

	/*
	 * Checks that .num_layers is large enough for at least
	 * LANDLOCK_MAX_NUM_LAYERS layers.
	 */
	BUILD_BUG_ON(rule.num_layers < LANDLOCK_MAX_NUM_LAYERS);
}

@@ -290,6 +294,10 @@ static void build_check_layer(void)
		.access = ~0,
	};

	/*
	 * Checks that .level and .access are large enough to contain their expected
	 * maximum values.
	 */
	BUILD_BUG_ON(layer.level < LANDLOCK_MAX_NUM_LAYERS);
	BUILD_BUG_ON(layer.access < LANDLOCK_MASK_ACCESS_FS);
}
@@ -644,8 +652,8 @@ bool landlock_unmask_layers(const struct landlock_rule *const rule,
		bool is_empty;

		/*
		 * Records in @layer_masks which layer grants access to each
		 * requested access.
		 * Records in @layer_masks which layer grants access to each requested
		 * access: bit cleared if the related layer grants access.
		 */
		is_empty = true;
		for_each_set_bit(access_bit, &access_req, masks_array_size) {
+1 −1
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ struct landlock_hierarchy;
 */
struct landlock_layer {
	/**
	 * @level: Position of this layer in the layer stack.
	 * @level: Position of this layer in the layer stack.  Starts from 1.
	 */
	u16 level;
	/**
Loading