Skip to content

Commit eee654c

Browse files
committed
Merge tag 'landlock-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux
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
2 parents 10003ff + 54f9baf commit eee654c

7 files changed

Lines changed: 1536 additions & 27 deletions

File tree

Documentation/security/landlock.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Landlock LSM: kernel documentation
77
==================================
88

99
:Author: Mickaël Salaün
10-
:Date: March 2025
10+
:Date: September 2025
1111

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

113+
Process credential
114+
------------------
115+
116+
.. kernel-doc:: security/landlock/cred.h
117+
:identifiers:
118+
113119
Ruleset and domain
114120
------------------
115121

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

137+
.. kernel-doc:: security/landlock/domain.h
138+
:identifiers:
139+
131140
Additional documentation
132141
========================
133142

security/landlock/errata/abi-1.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
3+
/**
4+
* DOC: erratum_3
5+
*
6+
* Erratum 3: Disconnected directory handling
7+
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8+
*
9+
* This fix addresses an issue with disconnected directories that occur when a
10+
* directory is moved outside the scope of a bind mount. The change ensures
11+
* that evaluated access rights include both those from the disconnected file
12+
* hierarchy down to its filesystem root and those from the related mount point
13+
* hierarchy. This prevents access right widening through rename or link
14+
* actions.
15+
*/
16+
LANDLOCK_ERRATUM(3)

security/landlock/fs.c

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,8 @@ static void test_is_eacces_with_write(struct kunit *const test)
714714
* is_access_to_paths_allowed - Check accesses for requests with a common path
715715
*
716716
* @domain: Domain to check against.
717-
* @path: File hierarchy to walk through.
717+
* @path: File hierarchy to walk through. For refer checks, this would be
718+
* the common mountpoint.
718719
* @access_request_parent1: Accesses to check, once @layer_masks_parent1 is
719720
* equal to @layer_masks_parent2 (if any). This is tied to the unique
720721
* 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(
837838
* restriction.
838839
*/
839840
while (true) {
840-
struct dentry *parent_dentry;
841841
const struct landlock_rule *rule;
842842

843843
/*
@@ -909,21 +909,33 @@ static bool is_access_to_paths_allowed(
909909
break;
910910
}
911911
}
912+
912913
if (unlikely(IS_ROOT(walker_path.dentry))) {
913-
/*
914-
* Stops at disconnected root directories. Only allows
915-
* access to internal filesystems (e.g. nsfs, which is
916-
* reachable through /proc/<pid>/ns/<namespace>).
917-
*/
918-
if (walker_path.mnt->mnt_flags & MNT_INTERNAL) {
914+
if (likely(walker_path.mnt->mnt_flags & MNT_INTERNAL)) {
915+
/*
916+
* Stops and allows access when reaching disconnected root
917+
* directories that are part of internal filesystems (e.g. nsfs,
918+
* which is reachable through /proc/<pid>/ns/<namespace>).
919+
*/
919920
allowed_parent1 = true;
920921
allowed_parent2 = true;
922+
break;
921923
}
922-
break;
924+
925+
/*
926+
* We reached a disconnected root directory from a bind mount.
927+
* Let's continue the walk with the mount point we missed.
928+
*/
929+
dput(walker_path.dentry);
930+
walker_path.dentry = walker_path.mnt->mnt_root;
931+
dget(walker_path.dentry);
932+
} else {
933+
struct dentry *const parent_dentry =
934+
dget_parent(walker_path.dentry);
935+
936+
dput(walker_path.dentry);
937+
walker_path.dentry = parent_dentry;
923938
}
924-
parent_dentry = dget_parent(walker_path.dentry);
925-
dput(walker_path.dentry);
926-
walker_path.dentry = parent_dentry;
927939
}
928940
path_put(&walker_path);
929941

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

1065-
/* We should not reach a root other than @mnt_root. */
1066-
if (dir == mnt_root || WARN_ON_ONCE(IS_ROOT(dir)))
1080+
/*
1081+
* Stops at the mount point or the filesystem root for a disconnected
1082+
* directory.
1083+
*/
1084+
if (dir == mnt_root || unlikely(IS_ROOT(dir)))
10671085
break;
10681086

10691087
parent_dentry = dget_parent(dir);

security/landlock/ruleset.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ static void build_check_rule(void)
8383
.num_layers = ~0,
8484
};
8585

86+
/*
87+
* Checks that .num_layers is large enough for at least
88+
* LANDLOCK_MAX_NUM_LAYERS layers.
89+
*/
8690
BUILD_BUG_ON(rule.num_layers < LANDLOCK_MAX_NUM_LAYERS);
8791
}
8892

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

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

646654
/*
647-
* Records in @layer_masks which layer grants access to each
648-
* requested access.
655+
* Records in @layer_masks which layer grants access to each requested
656+
* access: bit cleared if the related layer grants access.
649657
*/
650658
is_empty = true;
651659
for_each_set_bit(access_bit, &access_req, masks_array_size) {

security/landlock/ruleset.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ struct landlock_hierarchy;
2727
*/
2828
struct landlock_layer {
2929
/**
30-
* @level: Position of this layer in the layer stack.
30+
* @level: Position of this layer in the layer stack. Starts from 1.
3131
*/
3232
u16 level;
3333
/**

tools/testing/selftests/landlock/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
CFLAGS += -Wall -O2 $(KHDR_INCLUDES)
66

7-
LOCAL_HDRS += common.h
7+
LOCAL_HDRS += $(wildcard *.h)
88

99
src_test := $(wildcard *_test.c)
1010

0 commit comments

Comments
 (0)