* Re: [PATCH] keys: Pin request_key_auth payload in instantiate paths
From: Jarkko Sakkinen @ 2026-06-10 10:16 UTC (permalink / raw)
To: Shaomin Chen
Cc: keyrings, linux-security-module, linux-kernel, David Howells,
Paul Moore, James Morris, Serge E. Hallyn
In-Reply-To: <aiZWPSJZyFq4nmxf@kernel.org>
On Mon, Jun 08, 2026 at 08:42:21AM +0300, Jarkko Sakkinen wrote:
> On Mon, Jun 08, 2026 at 08:29:11AM +0300, Jarkko Sakkinen wrote:
> > On Mon, Jun 08, 2026 at 06:10:23AM +0300, Jarkko Sakkinen wrote:
> > > On Mon, Jun 08, 2026 at 06:06:21AM +0300, Jarkko Sakkinen wrote:
> > > > On Tue, May 26, 2026 at 10:48:38AM +0800, Shaomin Chen wrote:
> > > > > keyctl_instantiate_key_common() reads request_key_auth from the assumed
> > > > > auth key before copying an instantiation payload from userspace. The copy
> > > > > can fault and sleep. If the request completes and revokes the auth key in
> > > > > that window, the auth payload can be detached and freed before the
> > > > > instantiate path uses it again.
> > > > >
> > > > > A request-key helper reproducer can trigger this race. One helper child
> > > > > blocks in KEYCTL_INSTANTIATE_IOV while the original helper instantiates the
> > > > > requested key and returns. KASAN then reports a use-after-free from the
> > > > > stale request_key_auth payload in keyctl_instantiate_key_common().
> > > > >
> > > > > Give request_key_auth payloads a refcount. Take a payload reference while
> > > >
> > > > Please, name concrete things accurately. I.e. 'usage' in this case. If
> > > > you have a name, use it instead of obfuscating generalizations.
> > > >
> > > > > authkey->sem stabilizes the payload and revocation state. Hold that
> > > > > reference across the instantiate and reject paths. Drop the auth key
> > > > > owning reference from revoke and destroy.
> > > > >
> > > > > Reported-by: Shaomin Chen <eeesssooo020@gmail.com>
> > > > > Closes: https://lore.kernel.org/r/20260519144403.436694-1-eeesssooo020@gmail.com
> > > > > Signed-off-by: Shaomin Chen <eeesssooo020@gmail.com>
> > > > > ---
> > > > > include/keys/request_key_auth-type.h | 2 ++
> > > > > security/keys/internal.h | 2 ++
> > > > > security/keys/keyctl.c | 24 +++++++++++++++-----
> > > > > security/keys/request_key_auth.c | 33 ++++++++++++++++++++++++++--
> > > > > 4 files changed, 53 insertions(+), 8 deletions(-)
> > > >
> > > > So first, couple of things.
> > > >
> > > > I'm not going to test not that well documented involving OOT driver.
> > >
> > > Oops, sorry typo. "not that well documented reproducer" :-)
> > >
> > > But it is cool we just then need to draw the picture.
> >
> > I think I got this:
> >
> > A: request_key() B: KEYCTL_INSTANTIATE_IOV
> > ---------------- -------------------------
> > create auth key
> > store rka in auth key
> > wait for helper
> > get auth key
> > load rka from auth key
> > copy user payload
> > sleep on #PF
> > helper completed
> > detach and free rka
> > destroy auth key
> > wake up
> > use rka->target_key
> > **USE-AFTER-FREE**
> >
> > So nothing really complicated here, is there?
>
> Send v2 with the code changes that I proposed as we want to the change
> as ergonomic as possible.
>
> Use this as the commit message:
>
> keys: Pin request_key_auth payload in instantiate paths
>
> A: request_key() B: KEYCTL_INSTANTIATE_IOV
> ---------------- -------------------------
> create auth key
> store rka in auth key
> wait for helper
> get auth key
> load rka from auth key
> copy user payload
> sleep on #PF
>
> helper completed
> detach and free rka
> destroy auth key
> wake up
> use rka->target_key
> **USE-AFTER-FREE**
>
> Give request_key_auth payloads a refcount. Take a payload reference while
> authkey->sem stabilizes the payload and revocation state. Hold that
> reference across the instantiate and reject paths. Drop the auth key
> owning reference from revoke and destroy.
>
> [jarkko: Replaced the first two paragraphs of text with a concurrency scenario.]
>
> And it includes also the remark at the end.
>
> BR, Jarkko
Nothing heard so I pushed:
https://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd.git/commit/?h=for-next-keys&id=9feb0bb3468e863b2b82a2eabfaeec4c7c44b90c
It has the commit message change.
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v2] keys: prevent slab cache merging for key_jar
From: Jarkko Sakkinen @ 2026-06-10 9:57 UTC (permalink / raw)
To: Mohammed EL Kadiri
Cc: dhowells, paul, jmorris, serge, kees, vbabka, keyrings,
linux-security-module, linux-hardening, linux-kernel
In-Reply-To: <20260610065052.9120-1-med08elkadiri@gmail.com>
On Wed, Jun 10, 2026 at 07:50:52AM +0100, Mohammed EL Kadiri wrote:
> Add SLAB_NO_MERGE to key_jar to prevent the allocator from merging it
> with other similarly-sized caches. This hardens struct key isolation by
> ensuring dedicated slab pages.
>
> Acked-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
> Signed-off-by: Mohammed EL Kadiri <med08elkadiri@gmail.com>
> ---
> security/keys/key.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/security/keys/key.c b/security/keys/key.c
> index 3bbdde778631..592b65cf8539 100644
> --- a/security/keys/key.c
> +++ b/security/keys/key.c
> @@ -1275,7 +1275,7 @@ void __init key_init(void)
> {
> /* allocate a slab in which we can store keys */
> key_jar = kmem_cache_create("key_jar", sizeof(struct key),
> - 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
> + 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_NO_MERGE, NULL);
>
> /* add the special key types */
> list_add_tail(&key_type_keyring.link, &key_types_list);
> --
> 2.43.0
>
I swapped the commit.
https://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd.git/log/?h=for-next-keys
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v2 1/3] landlock: Require LANDLOCK_ACCESS_FS_MAKE_WHITEOUT for RENAME_WHITEOUT
From: Günther Noack @ 2026-06-10 9:29 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Christian Brauner, linux-security-module, Paul Moore,
Amir Goldstein, Miklos Szeredi, Serge Hallyn, Stephen Smalley
In-Reply-To: <20260609.pait5oaTheHi@digikod.net>
On Tue, Jun 09, 2026 at 06:09:51PM +0200, Mickaël Salaün wrote:
> On Wed, May 13, 2026 at 06:05:50PM +0200, Günther Noack wrote:
> > diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> > index c1ecfe239032..09de6ba5c3a3 100644
> > --- a/security/landlock/fs.c
> > +++ b/security/landlock/fs.c
> > @@ -1519,6 +1519,21 @@ static int hook_path_rename(const struct path *const old_dir,
> > const unsigned int flags)
> > {
> > /* old_dir refers to old_dentry->d_parent and new_dir->mnt */
> > + if (flags & RENAME_WHITEOUT) {
> > + int err;
> > +
> > + /*
> > + * Rename with RENAME_WHITEOUT creates a whiteout object in the
> > + * old location, so we check the access right for creating that.
> > + *
> > + * See Documentation/filesystems/overlayfs.rst and renameat2(2).
> > + */
> > + err = current_check_access_path(
> > + old_dir, LANDLOCK_ACCESS_FS_MAKE_WHITEOUT);
>
> We should not need a second path walk, even if whiteouts are rare.
> Please propose another way.
I sent a V3 with that implemented differently:
https://lore.kernel.org/all/20260610092318.3868884-1-gnoack@google.com/
The tradeoff is that it complicates the common current_check_refer_path() to
solve this fringe use case. In my understanding, the only software using this
is the FUSE OverlayFS implementation.
See the "tradeoffs" section in the V2 cover letter:
https://lore.kernel.org/all/20260513160552.4022649-1-gnoack@google.com/
I slightly prefer V2, but am OK with either variant if needed. Please pick
the one that makes more sense to you.
—Günther
^ permalink raw reply
* [PATCH v3 3/3] selftests/landlock: Test OverlayFS renames w/o LANDLOCK_ACCESS_FS_MAKE_WHITEOUT
From: Günther Noack @ 2026-06-10 9:23 UTC (permalink / raw)
To: Mickaël Salaün, Christian Brauner
Cc: linux-security-module, Paul Moore, Amir Goldstein, Miklos Szeredi,
Serge Hallyn, Stephen Smalley, Günther Noack
In-Reply-To: <20260610092318.3868884-1-gnoack@google.com>
Even though OverlayFS uses vfs_rename() with RENAME_WHITEOUT, and even
though RENAME_WHITEOUT requires LANDLOCK_ACCESS_FS_MAKE_WHITEOUT, a process
that renames files in an OverlayFS can do so without having the
LANDLOCK_ACCESS_FS_MAKE_WHITEOUT right in that location.
This works, and is supposed to work, because OverlayFS uses the credentials
determined at mount time for the internal vfs_rename() operation. -- The
rename happens with the credentials of the user who mounted the OverlayFS.
Signed-off-by: Günther Noack <gnoack@google.com>
---
tools/testing/selftests/landlock/fs_test.c | 31 ++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index bdad92195f62..0c29887278d0 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -6963,6 +6963,37 @@ TEST_F_FORK(layout2_overlay, same_content_different_file)
}
}
+TEST_F_FORK(layout2_overlay, rename_in_overlay_without_make_whiteout)
+{
+ struct stat st;
+ const char *merge_fl1_renamed = MERGE_DATA "/fl1_renamed";
+
+ if (self->skip_test)
+ SKIP(return, "overlayfs is not supported (test)");
+
+ enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_WHITEOUT, NULL);
+
+ /*
+ * Execute a regular file rename within OverlayFS.
+ * merge_fl1 originates from lower layer, so this triggers a copy-up
+ * and creation of a whiteout in the upper layer.
+ */
+ EXPECT_EQ(0, rename(merge_fl1, merge_fl1_renamed));
+
+ /* Check that the rename worked. */
+ EXPECT_EQ(0, stat(merge_fl1_renamed, &st));
+ EXPECT_EQ(-1, stat(merge_fl1, &st));
+ EXPECT_EQ(ENOENT, errno);
+
+ /*
+ * Check that the whiteout object on the underlying "upper" filesystem
+ * exists after the rename. This is OK because it was done with the
+ * credentials of the OverlayFS.
+ */
+ EXPECT_EQ(0, stat(UPPER_DATA "/fl1", &st));
+ EXPECT_TRUE(S_ISCHR(st.st_mode));
+ EXPECT_EQ(0, st.st_rdev);
+}
FIXTURE(layout3_fs)
{
--
2.54.0.1099.g489fc7bff1-goog
^ permalink raw reply related
* [PATCH v3 2/3] selftests/landlock: Add test for RENAME_WHITEOUT denial
From: Günther Noack @ 2026-06-10 9:23 UTC (permalink / raw)
To: Mickaël Salaün, Christian Brauner
Cc: linux-security-module, Paul Moore, Amir Goldstein, Miklos Szeredi,
Serge Hallyn, Stephen Smalley, Günther Noack
In-Reply-To: <20260610092318.3868884-1-gnoack@google.com>
Add a test to check that renames with RENAME_WHITEOUT are guarded by
LANDLOCK_ACCESS_FS_MAKE_WHITEOUT.
Signed-off-by: Günther Noack <gnoack@google.com>
---
tools/testing/selftests/landlock/fs_test.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index 53d1b659849f..bdad92195f62 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -2248,6 +2248,19 @@ TEST_F_FORK(layout1, rename_file)
RENAME_EXCHANGE));
}
+TEST_F_FORK(layout1, rename_whiteout_denied)
+{
+ enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_WHITEOUT, NULL);
+
+ /*
+ * Try to rename a file with RENAME_WHITEOUT.
+ * file1_s3d3 is in dir_s3d2 (tmpfs), so it supports RENAME_WHITEOUT.
+ */
+ EXPECT_EQ(-1, renameat2(AT_FDCWD, file1_s3d3, AT_FDCWD,
+ TMP_DIR "/s3d1/s3d2/s3d3/f2", RENAME_WHITEOUT));
+ EXPECT_EQ(EACCES, errno);
+}
+
TEST_F_FORK(layout1, rename_dir)
{
const struct rule rules[] = {
@@ -6950,6 +6963,7 @@ TEST_F_FORK(layout2_overlay, same_content_different_file)
}
}
+
FIXTURE(layout3_fs)
{
bool has_created_dir;
--
2.54.0.1099.g489fc7bff1-goog
^ permalink raw reply related
* [PATCH v3 1/3] landlock: Require LANDLOCK_ACCESS_FS_MAKE_WHITEOUT for RENAME_WHITEOUT
From: Günther Noack @ 2026-06-10 9:23 UTC (permalink / raw)
To: Mickaël Salaün, Christian Brauner
Cc: linux-security-module, Paul Moore, Amir Goldstein, Miklos Szeredi,
Serge Hallyn, Stephen Smalley, Günther Noack
In-Reply-To: <20260610092318.3868884-1-gnoack@google.com>
renameat2(2) with the RENAME_WHITEOUT flag places a whiteout character
device file in the source file location in place of the moved file.
This creates a directory entry even in cases where all
LANDLOCK_ACCESS_FS_MAKE_* rights are denied.
Introduce the LANDLOCK_ACCESS_FS_MAKE_WHITEOUT right, which is checked
for the origin directory if RENAME_WHITEOUT is passed.
This does not affect normal renames within layered OverlayFS mounts:
When OverlayFS invokes rename with RENAME_WHITEOUT as part of a
"normal" rename operation, it does so in ovl_rename() using the
credentials that were set at the time of mounting the OverlayFS.
Bump the Landlock ABI version to 10.
Suggested-by: Christian Brauner <brauner@kernel.org>
Suggested-by: Mickaël Salaün <mic@digikod.net>
Signed-off-by: Günther Noack <gnoack@google.com>
---
include/uapi/linux/landlock.h | 3 +++
security/landlock/audit.c | 1 +
security/landlock/fs.c | 17 ++++++++++++++---
security/landlock/limits.h | 2 +-
security/landlock/syscalls.c | 2 +-
tools/testing/selftests/landlock/base_test.c | 4 ++--
tools/testing/selftests/landlock/fs_test.c | 5 +++--
7 files changed, 25 insertions(+), 9 deletions(-)
diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index 10a346e55e95..1f8a1d6d25f1 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -328,6 +328,8 @@ struct landlock_net_port_attr {
*
* If multiple requirements are not met, the ``EACCES`` error code takes
* precedence over ``EXDEV``.
+ * - %LANDLOCK_ACCESS_FS_MAKE_WHITEOUT: Create a whiteout object through
+ * :manpage:`rename(2)` with ``RENAME_WHITEOUT``.
*
* .. warning::
*
@@ -356,6 +358,7 @@ struct landlock_net_port_attr {
#define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14)
#define LANDLOCK_ACCESS_FS_IOCTL_DEV (1ULL << 15)
#define LANDLOCK_ACCESS_FS_RESOLVE_UNIX (1ULL << 16)
+#define LANDLOCK_ACCESS_FS_MAKE_WHITEOUT (1ULL << 17)
/* clang-format on */
/**
diff --git a/security/landlock/audit.c b/security/landlock/audit.c
index 8d0edf94037d..09c97083f599 100644
--- a/security/landlock/audit.c
+++ b/security/landlock/audit.c
@@ -38,6 +38,7 @@ static const char *const fs_access_strings[] = {
[BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE)] = "fs.truncate",
[BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV)] = "fs.ioctl_dev",
[BIT_INDEX(LANDLOCK_ACCESS_FS_RESOLVE_UNIX)] = "fs.resolve_unix",
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_WHITEOUT)] = "fs.make_whiteout",
};
static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS);
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index c1ecfe239032..67810d5242b2 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -1080,6 +1080,7 @@ static bool collect_domain_accesses(const struct landlock_ruleset *const domain,
* @new_dentry: Destination file or directory.
* @removable: Sets to true if it is a rename operation.
* @exchange: Sets to true if it is a rename operation with RENAME_EXCHANGE.
+ * @whiteout: Sets to true if it is a rename operation with RENAME_WHITEOUT.
*
* Because of its unprivileged constraints, Landlock relies on file hierarchies
* (and not only inodes) to tie access rights to files. Being able to link or
@@ -1127,7 +1128,8 @@ static bool collect_domain_accesses(const struct landlock_ruleset *const domain,
static int current_check_refer_path(struct dentry *const old_dentry,
const struct path *const new_dir,
struct dentry *const new_dentry,
- const bool removable, const bool exchange)
+ const bool removable, const bool exchange,
+ const bool whiteout)
{
const struct landlock_cred_security *const subject =
landlock_get_applicable_subject(current_cred(), any_fs, NULL);
@@ -1159,6 +1161,14 @@ static int current_check_refer_path(struct dentry *const old_dentry,
access_request_parent2 |= maybe_remove(new_dentry);
}
+ /*
+ * In case of renameat2(2) with RENAME_WHITEOUT, a whiteout object is
+ * created in the source location, so we require an additional access
+ * right there.
+ */
+ if (whiteout)
+ access_request_parent1 |= LANDLOCK_ACCESS_FS_MAKE_WHITEOUT;
+
/* The mount points are the same for old and new paths, cf. EXDEV. */
if (old_dentry->d_parent == new_dir->dentry) {
/*
@@ -1509,7 +1519,7 @@ static int hook_path_link(struct dentry *const old_dentry,
struct dentry *const new_dentry)
{
return current_check_refer_path(old_dentry, new_dir, new_dentry, false,
- false);
+ false, false);
}
static int hook_path_rename(const struct path *const old_dir,
@@ -1520,7 +1530,8 @@ static int hook_path_rename(const struct path *const old_dir,
{
/* old_dir refers to old_dentry->d_parent and new_dir->mnt */
return current_check_refer_path(old_dentry, new_dir, new_dentry, true,
- !!(flags & RENAME_EXCHANGE));
+ !!(flags & RENAME_EXCHANGE),
+ !!(flags & RENAME_WHITEOUT));
}
static int hook_path_mkdir(const struct path *const dir,
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index b454ad73b15e..e59378e8e897 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -19,7 +19,7 @@
#define LANDLOCK_MAX_NUM_LAYERS 16
#define LANDLOCK_MAX_NUM_RULES U32_MAX
-#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_RESOLVE_UNIX
+#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_MAKE_WHITEOUT
#define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
#define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index accfd2e5a0cd..d45469d5d464 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -166,7 +166,7 @@ static const struct file_operations ruleset_fops = {
* If the change involves a fix that requires userspace awareness, also update
* the errata documentation in Documentation/userspace-api/landlock.rst .
*/
-const int landlock_abi_version = 9;
+const int landlock_abi_version = 10;
/**
* sys_landlock_create_ruleset - Create a new ruleset
diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
index 30d37234086c..6c8113c2ded1 100644
--- a/tools/testing/selftests/landlock/base_test.c
+++ b/tools/testing/selftests/landlock/base_test.c
@@ -76,8 +76,8 @@ TEST(abi_version)
const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
};
- ASSERT_EQ(9, landlock_create_ruleset(NULL, 0,
- LANDLOCK_CREATE_RULESET_VERSION));
+ ASSERT_EQ(10, landlock_create_ruleset(NULL, 0,
+ LANDLOCK_CREATE_RULESET_VERSION));
ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
LANDLOCK_CREATE_RULESET_VERSION));
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index cdb47fc1fc0a..53d1b659849f 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -579,7 +579,7 @@ TEST_F_FORK(layout1, inval)
LANDLOCK_ACCESS_FS_IOCTL_DEV | \
LANDLOCK_ACCESS_FS_RESOLVE_UNIX)
-#define ACCESS_LAST LANDLOCK_ACCESS_FS_RESOLVE_UNIX
+#define ACCESS_LAST LANDLOCK_ACCESS_FS_MAKE_WHITEOUT
#define ACCESS_ALL ( \
ACCESS_FILE | \
@@ -593,7 +593,8 @@ TEST_F_FORK(layout1, inval)
LANDLOCK_ACCESS_FS_MAKE_FIFO | \
LANDLOCK_ACCESS_FS_MAKE_BLOCK | \
LANDLOCK_ACCESS_FS_MAKE_SYM | \
- LANDLOCK_ACCESS_FS_REFER)
+ LANDLOCK_ACCESS_FS_REFER | \
+ LANDLOCK_ACCESS_FS_MAKE_WHITEOUT)
/* clang-format on */
--
2.54.0.1099.g489fc7bff1-goog
^ permalink raw reply related
* [PATCH v3 0/3] landlock: Restrict renameat2 with RENAME_WHITEOUT
From: Günther Noack @ 2026-06-10 9:23 UTC (permalink / raw)
To: Mickaël Salaün, Christian Brauner
Cc: linux-security-module, Paul Moore, Amir Goldstein, Miklos Szeredi,
Serge Hallyn, Stephen Smalley, Günther Noack
Hello!
As discussed in [1], the renameat2() syscall's RENAME_WHITEOUT flag allows
the creation of chardev directory entries with major=minor=0 as "whiteout
objects" in the location of the rename source file [2].
This functionality is available even without having any OverlayFS mounted
and can be invoked with the regular renameat2(2) syscall [3].
In V1 [5], it was discussed that whiteout objects are not the same as
character devices, and should therefore be guarded with a separate access
right. We are therefore guarding the operation with the new access right
LANDLOCK_ACCESS_FS_MAKE_WHITEOUT now.
By introducing a new access right, that change is also exposed by
incrementing the ABI level and does not require a Landlock erratum.
Motivation
==========
The RENAME_WHITEOUT flag side-steps all of the existing Landlock access
rights, which are designed to restrict the creation of directory entries.
It is desirable to restrict that.
This patch set fixes that by adding a check in Landlock's path_rename hook.
[1] https://lore.kernel.org/all/adUBCQXrt7kmgqJT@google.com/
[2] https://docs.kernel.org/filesystems/overlayfs.html#whiteouts-and-opaque-directories
[3] https://man7.org/linux/man-pages/man2/renameat2.2.html#DESCRIPTION
[4] https://codesearch.debian.net/search?q=rename.*RENAME_WHITEOUT&literal=0
[5] https://lore.kernel.org/all/20260411090944.3131168-2-gnoack@google.com/
Changelog
=========
v3:
- Do LANDLOCK_ACCESS_FS_MAKE_WHITEOUT check as part of
current_check_refer_path().
v2:
- Introduce LANDLOCK_ACCESS_FS_MAKE_WHITEOUT access right
and guard it with that.
- Bump ABI version
- https://lore.kernel.org/all/20260513160552.4022649-1-gnoack@google.com/
v1:
- initial version
https://lore.kernel.org/all/20260411090944.3131168-2-gnoack@google.com/
Günther Noack (3):
landlock: Require LANDLOCK_ACCESS_FS_MAKE_WHITEOUT for RENAME_WHITEOUT
selftests/landlock: Add test for RENAME_WHITEOUT denial
selftests/landlock: Test OverlayFS renames w/o
LANDLOCK_ACCESS_FS_MAKE_WHITEOUT
include/uapi/linux/landlock.h | 3 ++
security/landlock/audit.c | 1 +
security/landlock/fs.c | 17 +++++--
security/landlock/limits.h | 2 +-
security/landlock/syscalls.c | 2 +-
tools/testing/selftests/landlock/base_test.c | 4 +-
tools/testing/selftests/landlock/fs_test.c | 50 +++++++++++++++++++-
7 files changed, 70 insertions(+), 9 deletions(-)
Range-diff against v2:
1: 1f2b7f49b927 ! 1: 4a8c3fb9e707 landlock: Require LANDLOCK_ACCESS_FS_MAKE_WHITEOUT for RENAME_WHITEOUT
@@ security/landlock/audit.c: static const char *const fs_access_strings[] = {
static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS);
## security/landlock/fs.c ##
+@@ security/landlock/fs.c: static bool collect_domain_accesses(const struct landlock_ruleset *const domain,
+ * @new_dentry: Destination file or directory.
+ * @removable: Sets to true if it is a rename operation.
+ * @exchange: Sets to true if it is a rename operation with RENAME_EXCHANGE.
++ * @whiteout: Sets to true if it is a rename operation with RENAME_WHITEOUT.
+ *
+ * Because of its unprivileged constraints, Landlock relies on file hierarchies
+ * (and not only inodes) to tie access rights to files. Being able to link or
+@@ security/landlock/fs.c: static bool collect_domain_accesses(const struct landlock_ruleset *const domain,
+ static int current_check_refer_path(struct dentry *const old_dentry,
+ const struct path *const new_dir,
+ struct dentry *const new_dentry,
+- const bool removable, const bool exchange)
++ const bool removable, const bool exchange,
++ const bool whiteout)
+ {
+ const struct landlock_cred_security *const subject =
+ landlock_get_applicable_subject(current_cred(), any_fs, NULL);
+@@ security/landlock/fs.c: static int current_check_refer_path(struct dentry *const old_dentry,
+ access_request_parent2 |= maybe_remove(new_dentry);
+ }
+
++ /*
++ * In case of renameat2(2) with RENAME_WHITEOUT, a whiteout object is
++ * created in the source location, so we require an additional access
++ * right there.
++ */
++ if (whiteout)
++ access_request_parent1 |= LANDLOCK_ACCESS_FS_MAKE_WHITEOUT;
++
+ /* The mount points are the same for old and new paths, cf. EXDEV. */
+ if (old_dentry->d_parent == new_dir->dentry) {
+ /*
+@@ security/landlock/fs.c: static int hook_path_link(struct dentry *const old_dentry,
+ struct dentry *const new_dentry)
+ {
+ return current_check_refer_path(old_dentry, new_dir, new_dentry, false,
+- false);
++ false, false);
+ }
+
+ static int hook_path_rename(const struct path *const old_dir,
@@ security/landlock/fs.c: static int hook_path_rename(const struct path *const old_dir,
- const unsigned int flags)
{
/* old_dir refers to old_dentry->d_parent and new_dir->mnt */
-+ if (flags & RENAME_WHITEOUT) {
-+ int err;
-+
-+ /*
-+ * Rename with RENAME_WHITEOUT creates a whiteout object in the
-+ * old location, so we check the access right for creating that.
-+ *
-+ * See Documentation/filesystems/overlayfs.rst and renameat2(2).
-+ */
-+ err = current_check_access_path(
-+ old_dir, LANDLOCK_ACCESS_FS_MAKE_WHITEOUT);
-+ if (err)
-+ return err;
-+ }
-+
return current_check_refer_path(old_dentry, new_dir, new_dentry, true,
- !!(flags & RENAME_EXCHANGE));
+- !!(flags & RENAME_EXCHANGE));
++ !!(flags & RENAME_EXCHANGE),
++ !!(flags & RENAME_WHITEOUT));
}
+
+ static int hook_path_mkdir(const struct path *const dir,
## security/landlock/limits.h ##
@@
2: aa4e4aeb5884 = 2: 063646822083 selftests/landlock: Add test for RENAME_WHITEOUT denial
3: 6660d70a1eda = 3: 5d4606bc1e84 selftests/landlock: Test OverlayFS renames w/o LANDLOCK_ACCESS_FS_MAKE_WHITEOUT
--
2.54.0.1099.g489fc7bff1-goog
^ permalink raw reply
* [PATCH v2] keys: prevent slab cache merging for key_jar
From: Mohammed EL Kadiri @ 2026-06-10 6:50 UTC (permalink / raw)
To: dhowells, jarkko
Cc: paul, jmorris, serge, kees, vbabka, keyrings,
linux-security-module, linux-hardening, linux-kernel,
Mohammed EL Kadiri
Add SLAB_NO_MERGE to key_jar to prevent the allocator from merging it
with other similarly-sized caches. This hardens struct key isolation by
ensuring dedicated slab pages.
Acked-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
Signed-off-by: Mohammed EL Kadiri <med08elkadiri@gmail.com>
---
security/keys/key.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/security/keys/key.c b/security/keys/key.c
index 3bbdde778631..592b65cf8539 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -1275,7 +1275,7 @@ void __init key_init(void)
{
/* allocate a slab in which we can store keys */
key_jar = kmem_cache_create("key_jar", sizeof(struct key),
- 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
+ 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_NO_MERGE, NULL);
/* add the special key types */
list_add_tail(&key_type_keyring.link, &key_types_list);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v2 00/16] Bump minimum version of LLVM for building the kernel to 17.0.1
From: Nathan Chancellor @ 2026-06-09 23:28 UTC (permalink / raw)
To: Nicolas Schier, Bill Wendling, Justin Stitt, Nick Desaulniers,
Nathan Chancellor
Cc: linux-kernel, llvm, linux-kbuild, Jonathan Corbet, Shuah Khan,
linux-doc, Kees Cook, Gustavo A. R. Silva, linux-hardening,
linux-security-module, Rong Xu, Han Shen, Russell King,
Arnd Bergmann, linux-arm-kernel, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, linux-riscv, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Ard Biesheuvel, Peter Zijlstra
In-Reply-To: <20260517-bump-minimum-supported-llvm-version-to-17-v2-0-b3b8cda46bdd@kernel.org>
On Sun, 17 May 2026 13:05:03 -1000, Nathan Chancellor wrote:
> Bump minimum version of LLVM for building the kernel to 17.0.1
>
> The current minimum version of LLVM for building the kernel is 15.0.0.
> However, there are two deficiencies compared to GCC that were fixed in
> LLVM 17 that are starting to become more noticeable.
>
> The first was a bug in LLVM's scope checker [1], where all labels in a
> function were validated as potential targets of an asm goto statement,
> even if they were not listed in the asm goto statement as targets. This
> becomes particularly problematic when the cleanup attribute is used, as
>
> [...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/kbuild/linux.git kbuild-next
Thanks!
[01/16] kbuild: Bump minimum version of LLVM for building the kernel to 17.0.1
https://git.kernel.org/kbuild/c/ce3267a39a92b
[02/16] security/Kconfig.hardening: Remove tautological condition from CC_HAS_ZERO_CALL_USED_REGS
https://git.kernel.org/kbuild/c/813fe686e90b4
[03/16] security/Kconfig.hardening: Remove tautological condition from FORTIFY_SOURCE
https://git.kernel.org/kbuild/c/8ad2017578c99
[04/16] security/Kconfig.hardening: Remove tautological condition from CC_HAS_RANDSTRUCT
https://git.kernel.org/kbuild/c/9331258bc129a
[05/16] arch/Kconfig: Remove tautological conditions from HAS_LTO_CLANG
https://git.kernel.org/kbuild/c/2189cb1a80f06
[06/16] arch/Kconfig: Remove tautological condition from AUTOFDO_CLANG
https://git.kernel.org/kbuild/c/de0bf1e138fcd
[07/16] ARM: Drop tautological ld.lld conditions from ARCH_MULTI_V4{,T}
https://git.kernel.org/kbuild/c/48d229b6a48ae
[08/16] riscv: Remove tautological condition from selection of ARCH_SUPPORTS_CFI
https://git.kernel.org/kbuild/c/62c4af8689511
[09/16] riscv: Drop tautological condition from TOOLCHAIN_NEEDS_OLD_ISA_SPEC
https://git.kernel.org/kbuild/c/7e279976cf2a2
[10/16] scripts/Makefile.warn: Drop -Wformat handling for clang < 16
https://git.kernel.org/kbuild/c/2a35c63c6bc42
[11/16] x86/build: Drop unnecessary '-ffreestanding' addition to KBUILD_CFLAGS
https://git.kernel.org/kbuild/c/7b3281fcb43c5
[12/16] x86/module: Revert "Deal with GOT based stack cookie load on Clang < 17"
https://git.kernel.org/kbuild/c/12b7bf92bddd4
[13/16] x86/entry/vdso32: Remove conditional omission of '.cfi_offset eflags'
https://git.kernel.org/kbuild/c/4e7af20d0d104
[14/16] kbuild: Remove check for broken scoping with clang < 17 in CC_HAS_ASM_GOTO_OUTPUT
https://git.kernel.org/kbuild/c/f3de78cb19d12
[15/16] compiler-clang.h: Remove __cleanup -Wunused-variable workaround
https://git.kernel.org/kbuild/c/c69eaa687667e
[16/16] compiler-clang.h: Drop explicit version number from "all" diagnostic macro
https://git.kernel.org/kbuild/c/c919893eabb43
Please look out for regression or issue reports or other follow up
comments, as they may result in the patch/series getting dropped or
reverted. Patches applied to an "unstable" branch are accepted pending
wider testing in -next and any post-commit review; they will generally
be moved to the main branch in a week if no issues are found.
Best regards,
--
Cheers,
Nathan
^ permalink raw reply
* [PATCH 2/2] landlock: replace __sk_common struct sock field accesses
From: Matthieu Buffet @ 2026-06-09 21:15 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Matthieu Buffet, Günther Noack, Mikhail Ivanov, Tingmao Wang,
konstantin.meskhidze, linux-security-module
In-Reply-To: <20260609211511.85630-1-matthieu@buffet.re>
Use the proper macro to access __sk_common.skc_family like everywhere
else.
Signed-off-by: Matthieu Buffet <matthieu@buffet.re>
---
security/landlock/net.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/security/landlock/net.c b/security/landlock/net.c
index 111e58fd9325..fc2acf8bd898 100644
--- a/security/landlock/net.c
+++ b/security/landlock/net.c
@@ -71,7 +71,7 @@ static int current_check_access_socket(struct socket *const sock,
* The socket is not locked, so sk_family can change concurrently
* due to e.g. setsockopt(IPV6_ADDRFORM).
*/
- sock_family = READ_ONCE(sock->sk->__sk_common.skc_family);
+ sock_family = READ_ONCE(sock->sk->sk_family);
switch (address->sa_family) {
case AF_UNSPEC:
--
2.47.3
^ permalink raw reply related
* [PATCH 1/2] landlock: Fix unmarked concurrent access to socket family
From: Matthieu Buffet @ 2026-06-09 21:15 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Matthieu Buffet, Günther Noack, Mikhail Ivanov, Tingmao Wang,
konstantin.meskhidze, linux-security-module
In-Reply-To: <e561a8c3-de10-45ba-a931-27b9a5751dc9@buffet.re>
Socket family is read (twice) in a context where the socket is not
locked, so another thread can setsockopt(IPV6_ADDRFORM) to write it
concurrently. Add needed READ_ONCE() annotation.
Fixes: fff69fb03dde ("landlock: Support network rules with TCP bind and connect")
Signed-off-by: Matthieu Buffet <matthieu@buffet.re>
---
security/landlock/net.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/security/landlock/net.c b/security/landlock/net.c
index a38bdfcffc22..111e58fd9325 100644
--- a/security/landlock/net.c
+++ b/security/landlock/net.c
@@ -55,6 +55,7 @@ static int current_check_access_socket(struct socket *const sock,
const struct access_masks masks = {
.net = access_request,
};
+ unsigned short sock_family;
const struct landlock_cred_security *const subject =
landlock_get_applicable_subject(current_cred(), masks, NULL);
struct lsm_network_audit audit_net = {};
@@ -66,6 +67,12 @@ static int current_check_access_socket(struct socket *const sock,
if (addrlen < offsetofend(typeof(*address), sa_family))
return -EINVAL;
+ /*
+ * The socket is not locked, so sk_family can change concurrently
+ * due to e.g. setsockopt(IPV6_ADDRFORM).
+ */
+ sock_family = READ_ONCE(sock->sk->__sk_common.skc_family);
+
switch (address->sa_family) {
case AF_UNSPEC:
if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) {
@@ -102,7 +109,7 @@ static int current_check_access_socket(struct socket *const sock,
* these checks, but it is safer to return a proper
* error and test consistency thanks to kselftest.
*/
- if (sock->sk->__sk_common.skc_family == AF_INET) {
+ if (sock_family == AF_INET) {
const struct sockaddr_in *const sockaddr =
(struct sockaddr_in *)address;
@@ -180,7 +187,7 @@ static int current_check_access_socket(struct socket *const sock,
* check, but it is safer to return a proper error and test
* consistency thanks to kselftest.
*/
- if (address->sa_family != sock->sk->__sk_common.skc_family &&
+ if (address->sa_family != sock_family &&
address->sa_family != AF_UNSPEC)
return -EINVAL;
base-commit: 4c403b9ffc86358d5ae50e4121aaf541bdab04d8
--
2.47.3
^ permalink raw reply related
* Re: [PATCH v2 1/3] landlock: Require LANDLOCK_ACCESS_FS_MAKE_WHITEOUT for RENAME_WHITEOUT
From: Mickaël Salaün @ 2026-06-09 16:09 UTC (permalink / raw)
To: Günther Noack
Cc: Christian Brauner, linux-security-module, Paul Moore,
Amir Goldstein, Miklos Szeredi, Serge Hallyn, Stephen Smalley
In-Reply-To: <20260513160552.4022649-2-gnoack@google.com>
On Wed, May 13, 2026 at 06:05:50PM +0200, Günther Noack wrote:
> renameat2(2) with the RENAME_WHITEOUT flag places a whiteout character
> device file in the source file location in place of the moved file.
> This creates a directory entry even in cases where all
> LANDLOCK_ACCESS_FS_MAKE_* rights are denied.
>
> Introduce the LANDLOCK_ACCESS_FS_MAKE_WHITEOUT right, which is checked
> for the origin directory if RENAME_WHITEOUT is passed.
>
> This does not affect normal renames within layered OverlayFS mounts:
> When OverlayFS invokes rename with RENAME_WHITEOUT as part of a
> "normal" rename operation, it does so in ovl_rename() using the
> credentials that were set at the time of mounting the OverlayFS.
>
> Bump the Landlock ABI version to 10.
>
> Suggested-by: Christian Brauner <brauner@kernel.org>
> Suggested-by: Mickaël Salaün <mic@digikod.net>
> Signed-off-by: Günther Noack <gnoack@google.com>
> ---
> include/uapi/linux/landlock.h | 3 +++
> security/landlock/audit.c | 1 +
> security/landlock/fs.c | 15 +++++++++++++++
> security/landlock/limits.h | 2 +-
> security/landlock/syscalls.c | 2 +-
> tools/testing/selftests/landlock/base_test.c | 4 ++--
> tools/testing/selftests/landlock/fs_test.c | 5 +++--
> 7 files changed, 26 insertions(+), 6 deletions(-)
>
> diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
> index 10a346e55e95..1f8a1d6d25f1 100644
> --- a/include/uapi/linux/landlock.h
> +++ b/include/uapi/linux/landlock.h
> @@ -328,6 +328,8 @@ struct landlock_net_port_attr {
> *
> * If multiple requirements are not met, the ``EACCES`` error code takes
> * precedence over ``EXDEV``.
> + * - %LANDLOCK_ACCESS_FS_MAKE_WHITEOUT: Create a whiteout object through
> + * :manpage:`rename(2)` with ``RENAME_WHITEOUT``.
> *
> * .. warning::
> *
> @@ -356,6 +358,7 @@ struct landlock_net_port_attr {
> #define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14)
> #define LANDLOCK_ACCESS_FS_IOCTL_DEV (1ULL << 15)
> #define LANDLOCK_ACCESS_FS_RESOLVE_UNIX (1ULL << 16)
> +#define LANDLOCK_ACCESS_FS_MAKE_WHITEOUT (1ULL << 17)
> /* clang-format on */
>
> /**
> diff --git a/security/landlock/audit.c b/security/landlock/audit.c
> index 8d0edf94037d..09c97083f599 100644
> --- a/security/landlock/audit.c
> +++ b/security/landlock/audit.c
> @@ -38,6 +38,7 @@ static const char *const fs_access_strings[] = {
> [BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE)] = "fs.truncate",
> [BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV)] = "fs.ioctl_dev",
> [BIT_INDEX(LANDLOCK_ACCESS_FS_RESOLVE_UNIX)] = "fs.resolve_unix",
> + [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_WHITEOUT)] = "fs.make_whiteout",
> };
>
> static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS);
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index c1ecfe239032..09de6ba5c3a3 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -1519,6 +1519,21 @@ static int hook_path_rename(const struct path *const old_dir,
> const unsigned int flags)
> {
> /* old_dir refers to old_dentry->d_parent and new_dir->mnt */
> + if (flags & RENAME_WHITEOUT) {
> + int err;
> +
> + /*
> + * Rename with RENAME_WHITEOUT creates a whiteout object in the
> + * old location, so we check the access right for creating that.
> + *
> + * See Documentation/filesystems/overlayfs.rst and renameat2(2).
> + */
> + err = current_check_access_path(
> + old_dir, LANDLOCK_ACCESS_FS_MAKE_WHITEOUT);
We should not need a second path walk, even if whiteouts are rare.
Please propose another way.
> + if (err)
> + return err;
> + }
> +
> return current_check_refer_path(old_dentry, new_dir, new_dentry, true,
> !!(flags & RENAME_EXCHANGE));
> }
> diff --git a/security/landlock/limits.h b/security/landlock/limits.h
> index b454ad73b15e..e59378e8e897 100644
> --- a/security/landlock/limits.h
> +++ b/security/landlock/limits.h
> @@ -19,7 +19,7 @@
> #define LANDLOCK_MAX_NUM_LAYERS 16
> #define LANDLOCK_MAX_NUM_RULES U32_MAX
>
> -#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_RESOLVE_UNIX
> +#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_MAKE_WHITEOUT
> #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
> #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
>
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index accfd2e5a0cd..d45469d5d464 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -166,7 +166,7 @@ static const struct file_operations ruleset_fops = {
> * If the change involves a fix that requires userspace awareness, also update
> * the errata documentation in Documentation/userspace-api/landlock.rst .
> */
> -const int landlock_abi_version = 9;
> +const int landlock_abi_version = 10;
>
> /**
> * sys_landlock_create_ruleset - Create a new ruleset
> diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
> index 30d37234086c..6c8113c2ded1 100644
> --- a/tools/testing/selftests/landlock/base_test.c
> +++ b/tools/testing/selftests/landlock/base_test.c
> @@ -76,8 +76,8 @@ TEST(abi_version)
> const struct landlock_ruleset_attr ruleset_attr = {
> .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
> };
> - ASSERT_EQ(9, landlock_create_ruleset(NULL, 0,
> - LANDLOCK_CREATE_RULESET_VERSION));
> + ASSERT_EQ(10, landlock_create_ruleset(NULL, 0,
> + LANDLOCK_CREATE_RULESET_VERSION));
>
> ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
> LANDLOCK_CREATE_RULESET_VERSION));
> diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
> index cdb47fc1fc0a..53d1b659849f 100644
> --- a/tools/testing/selftests/landlock/fs_test.c
> +++ b/tools/testing/selftests/landlock/fs_test.c
> @@ -579,7 +579,7 @@ TEST_F_FORK(layout1, inval)
> LANDLOCK_ACCESS_FS_IOCTL_DEV | \
> LANDLOCK_ACCESS_FS_RESOLVE_UNIX)
>
> -#define ACCESS_LAST LANDLOCK_ACCESS_FS_RESOLVE_UNIX
> +#define ACCESS_LAST LANDLOCK_ACCESS_FS_MAKE_WHITEOUT
>
> #define ACCESS_ALL ( \
> ACCESS_FILE | \
> @@ -593,7 +593,8 @@ TEST_F_FORK(layout1, inval)
> LANDLOCK_ACCESS_FS_MAKE_FIFO | \
> LANDLOCK_ACCESS_FS_MAKE_BLOCK | \
> LANDLOCK_ACCESS_FS_MAKE_SYM | \
> - LANDLOCK_ACCESS_FS_REFER)
> + LANDLOCK_ACCESS_FS_REFER | \
> + LANDLOCK_ACCESS_FS_MAKE_WHITEOUT)
>
> /* clang-format on */
>
> --
> 2.54.0.563.g4f69b47b94-goog
>
>
^ permalink raw reply
* Re: [PATCH v2] tpm: use a map for tpm2_calc_ordinal_duration()
From: Jarkko Sakkinen @ 2026-06-09 15:44 UTC (permalink / raw)
To: Benoit HOUYERE
Cc: Serge E. Hallyn, linux-integrity@vger.kernel.org,
Frédéric Jouen, Peter Huewe, Jason Gunthorpe,
James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, open list, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, Laurent CHARPENTIER
In-Reply-To: <DB5PR10MB766844D5478D63272584A12AFE1C2@DB5PR10MB7668.EURPRD10.PROD.OUTLOOK.COM>
On Mon, Jun 08, 2026 at 02:46:28PM +0000, Benoit HOUYERE wrote:
> Hello Serge and Jarkko,
>
>
> We have detected a regression with this fix. Indeed, it miss one zero on TPM_LONG_LONG. Initial value was 300000 and not 30000.
>
> > + {TPM2_CC_CREATE_PRIMARY, 30000},
> > + {TPM2_CC_CREATE, 30000},
> > + {TPM2_CC_CREATE_LOADED, 30000},
>
> > +enum tpm2_durations {
> > TPM2_DURATION_SHORT = 20,
> > - TPM2_DURATION_MEDIUM = 750,
> > TPM2_DURATION_LONG = 2000,
> > - TPM2_DURATION_LONG_LONG = 300000,
> > TPM2_DURATION_DEFAULT = 120000,
> > };
>
> Best Regards, Cordialement, Cordialmente, Hälsningar, 最好的问候, Mit besten Grüßen, 真心を込めて, 진심으로
>
>
> Benoit HOUYERE | Tel: +33 6 14 22 81 30
> TPM specialist
This has been fixed in 478a3f949a43822dc6ce089345ae80e8dcde3300.
>
> -----Original Message-----
> From: Serge E. Hallyn <serge@hallyn.com>
> Sent: Friday, September 19, 2025 5:49 AM
> To: Jarkko Sakkinen <jarkko@kernel.org>
> Cc: linux-integrity@vger.kernel.org; Frédéric Jouen <fjouen@sealsq.com>; Peter Huewe <peterhuewe@gmx.de>; Jason Gunthorpe <jgg@ziepe.ca>; James Bottomley <James.Bottomley@hansenpartnership.com>; Mimi Zohar <zohar@linux.ibm.com>; David Howells <dhowells@redhat.com>; Paul Moore <paul@paul-moore.com>; James Morris <jmorris@namei.org>; Serge E. Hallyn <serge@hallyn.com>; open list <linux-kernel@vger.kernel.org>; open list:KEYS-TRUSTED <keyrings@vger.kernel.org>; open list:SECURITY SUBSYSTEM <linux-security-module@vger.kernel.org>
> Subject: Re: [PATCH v2] tpm: use a map for tpm2_calc_ordinal_duration()
>
> On Thu, Sep 18, 2025 at 10:30:18PM +0300, Jarkko Sakkinen wrote:
> > The current shenanigans for duration calculation introduce too much
> > complexity for a trivial problem, and further the code is hard to
> > patch and maintain.
> >
> > Address these issues with a flat look-up table, which is easy to
> > understand and patch. If leaf driver specific patching is required in
> > future, it is easy enough to make a copy of this table during driver
> > initialization and add the chip parameter back.
> >
> > 'chip->duration' is retained for TPM 1.x.
> >
> > As the first entry for this new behavior address TCG spec update
> > mentioned in this issue:
> >
> > https://github.com/raspberrypi/linux/issues/7054
> >
> > Therefore, for TPM_SelfTest the duration is set to 3000 ms.
> >
> > This does not categorize a as bug, given that this is introduced to
> > the spec after the feature was originally made.
> >
> > Cc: Frédéric Jouen <fjouen@sealsq.com>
> > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
>
> fwiw (which shouldn't be much) looks good to me, but two questions, one here and one below.
>
> First, it looks like in the existing code it is possible for a tpm2 chip to set its own timeouts and then set the TPM_CHIP_FLAG_HAVE_TIMEOUTS flag to avoid using the defaults, but I don't see anything using that in-tree. Is it possible that there are out of tree drivers that will be sabotaged here? Or am I misunderstanding that completely?
>
> > ---
> > v2:
> > - Add the missing msec_to_jiffies() calls.
> > - Drop redundant stuff.
> > ---
> > drivers/char/tpm/tpm-interface.c | 2 +-
> > drivers/char/tpm/tpm.h | 2 +-
> > drivers/char/tpm/tpm2-cmd.c | 127 ++++++++-----------------------
> > include/linux/tpm.h | 5 +-
> > 4 files changed, 37 insertions(+), 99 deletions(-)
> >
> > diff --git a/drivers/char/tpm/tpm-interface.c
> > b/drivers/char/tpm/tpm-interface.c
> > index b71725827743..c9f173001d0e 100644
> > --- a/drivers/char/tpm/tpm-interface.c
> > +++ b/drivers/char/tpm/tpm-interface.c
> > @@ -52,7 +52,7 @@ MODULE_PARM_DESC(suspend_pcr, unsigned long
> > tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) {
> > if (chip->flags & TPM_CHIP_FLAG_TPM2)
> > - return tpm2_calc_ordinal_duration(chip, ordinal);
> > + return tpm2_calc_ordinal_duration(ordinal);
> > else
> > return tpm1_calc_ordinal_duration(chip, ordinal); } diff --git
> > a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index
> > 7bb87fa5f7a1..2726bd38e5ac 100644
> > --- a/drivers/char/tpm/tpm.h
> > +++ b/drivers/char/tpm/tpm.h
> > @@ -299,7 +299,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32
> > property_id, ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip);
> > int tpm2_auto_startup(struct tpm_chip *chip); void
> > tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type); -unsigned
> > long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal);
> > +unsigned long tpm2_calc_ordinal_duration(u32 ordinal);
> > int tpm2_probe(struct tpm_chip *chip); int
> > tpm2_get_cc_attrs_tbl(struct tpm_chip *chip); int tpm2_find_cc(struct
> > tpm_chip *chip, u32 cc); diff --git a/drivers/char/tpm/tpm2-cmd.c
> > b/drivers/char/tpm/tpm2-cmd.c index 524d802ede26..7d77f6fbc152 100644
> > --- a/drivers/char/tpm/tpm2-cmd.c
> > +++ b/drivers/char/tpm/tpm2-cmd.c
> > @@ -28,120 +28,57 @@ static struct tpm2_hash tpm2_hash_map[] = {
> >
> > int tpm2_get_timeouts(struct tpm_chip *chip) {
> > - /* Fixed timeouts for TPM2 */
> > chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
> > chip->timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B);
> > chip->timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C);
> > chip->timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D);
> > -
> > - /* PTP spec timeouts */
> > - chip->duration[TPM_SHORT] = msecs_to_jiffies(TPM2_DURATION_SHORT);
> > - chip->duration[TPM_MEDIUM] = msecs_to_jiffies(TPM2_DURATION_MEDIUM);
> > - chip->duration[TPM_LONG] = msecs_to_jiffies(TPM2_DURATION_LONG);
> > -
> > - /* Key creation commands long timeouts */
> > - chip->duration[TPM_LONG_LONG] =
> > - msecs_to_jiffies(TPM2_DURATION_LONG_LONG);
> > -
> > chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
> > -
> > return 0;
> > }
> >
> > -/**
> > - * tpm2_ordinal_duration_index() - returns an index to the chip
> > duration table
> > - * @ordinal: TPM command ordinal.
> > - *
> > - * The function returns an index to the chip duration table
> > - * (enum tpm_duration), that describes the maximum amount of
> > - * time the chip could take to return the result for a particular ordinal.
> > - *
> > - * The values of the MEDIUM, and LONG durations are taken
> > - * from the PC Client Profile (PTP) specification (750, 2000 msec)
> > - *
> > - * LONG_LONG is for commands that generates keys which empirically
> > takes
> > - * a longer time on some systems.
> > - *
> > - * Return:
> > - * * TPM_MEDIUM
> > - * * TPM_LONG
> > - * * TPM_LONG_LONG
> > - * * TPM_UNDEFINED
> > +/*
> > + * Contains the maximum durations in milliseconds for TPM2 commands.
> > */
> > -static u8 tpm2_ordinal_duration_index(u32 ordinal) -{
> > - switch (ordinal) {
> > - /* Startup */
> > - case TPM2_CC_STARTUP: /* 144 */
> > - return TPM_MEDIUM;
> > -
> > - case TPM2_CC_SELF_TEST: /* 143 */
> > - return TPM_LONG;
> > -
> > - case TPM2_CC_GET_RANDOM: /* 17B */
> > - return TPM_LONG;
> > -
> > - case TPM2_CC_SEQUENCE_UPDATE: /* 15C */
> > - return TPM_MEDIUM;
> > - case TPM2_CC_SEQUENCE_COMPLETE: /* 13E */
> > - return TPM_MEDIUM;
> > - case TPM2_CC_EVENT_SEQUENCE_COMPLETE: /* 185 */
> > - return TPM_MEDIUM;
> > - case TPM2_CC_HASH_SEQUENCE_START: /* 186 */
> > - return TPM_MEDIUM;
> > -
> > - case TPM2_CC_VERIFY_SIGNATURE: /* 177 */
> > - return TPM_LONG_LONG;
> > -
> > - case TPM2_CC_PCR_EXTEND: /* 182 */
> > - return TPM_MEDIUM;
> > -
> > - case TPM2_CC_HIERARCHY_CONTROL: /* 121 */
> > - return TPM_LONG;
> > - case TPM2_CC_HIERARCHY_CHANGE_AUTH: /* 129 */
> > - return TPM_LONG;
> > -
> > - case TPM2_CC_GET_CAPABILITY: /* 17A */
> > - return TPM_MEDIUM;
> > -
> > - case TPM2_CC_NV_READ: /* 14E */
> > - return TPM_LONG;
> > -
> > - case TPM2_CC_CREATE_PRIMARY: /* 131 */
> > - return TPM_LONG_LONG;
> > - case TPM2_CC_CREATE: /* 153 */
> > - return TPM_LONG_LONG;
> > - case TPM2_CC_CREATE_LOADED: /* 191 */
> > - return TPM_LONG_LONG;
> > -
> > - default:
> > - return TPM_UNDEFINED;
> > - }
> > -}
> > +static const struct {
> > + unsigned long ordinal;
> > + unsigned long duration;
> > +} tpm2_ordinal_duration_map[] = {
> > + {TPM2_CC_STARTUP, 750},
> > + {TPM2_CC_SELF_TEST, 3000},
>
> I assume you intended to increase TPM2_CC_SELF_TEST from 2000 to 3000 here? But it's not mentioned in the commit, so making sure...
>
> > + {TPM2_CC_GET_RANDOM, 2000},
> > + {TPM2_CC_SEQUENCE_UPDATE, 750},
> > + {TPM2_CC_SEQUENCE_COMPLETE, 750},
> > + {TPM2_CC_EVENT_SEQUENCE_COMPLETE, 750},
> > + {TPM2_CC_HASH_SEQUENCE_START, 750},
> > + {TPM2_CC_VERIFY_SIGNATURE, 30000},
> > + {TPM2_CC_PCR_EXTEND, 750},
> > + {TPM2_CC_HIERARCHY_CONTROL, 2000},
> > + {TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000},
> > + {TPM2_CC_GET_CAPABILITY, 750},
> > + {TPM2_CC_NV_READ, 2000},
> > + {TPM2_CC_CREATE_PRIMARY, 30000},
> > + {TPM2_CC_CREATE, 30000},
> > + {TPM2_CC_CREATE_LOADED, 30000},
> > +};
> >
> > /**
> > - * tpm2_calc_ordinal_duration() - calculate the maximum command duration
> > - * @chip: TPM chip to use.
> > + * tpm2_calc_ordinal_duration() - Calculate the maximum command
> > + duration
> > * @ordinal: TPM command ordinal.
> > *
> > - * The function returns the maximum amount of time the chip could
> > take
> > - * to return the result for a particular ordinal in jiffies.
> > - *
> > - * Return: A maximal duration time for an ordinal in jiffies.
> > + * Returns the maximum amount of time the chip is expected by kernel
> > + to
> > + * take in jiffies.
> > */
> > -unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32
> > ordinal)
> > +unsigned long tpm2_calc_ordinal_duration(u32 ordinal)
> > {
> > - unsigned int index;
> > + int i;
> >
> > - index = tpm2_ordinal_duration_index(ordinal);
> > + for (i = 0; i < ARRAY_SIZE(tpm2_ordinal_duration_map); i++)
> > + if (ordinal == tpm2_ordinal_duration_map[i].ordinal)
> > + return msecs_to_jiffies(tpm2_ordinal_duration_map[i].duration);
> >
> > - if (index != TPM_UNDEFINED)
> > - return chip->duration[index];
> > - else
> > - return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
> > + return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
> > }
> >
> > -
> > struct tpm2_pcr_read_out {
> > __be32 update_cnt;
> > __be32 pcr_selects_cnt;
> > diff --git a/include/linux/tpm.h b/include/linux/tpm.h index
> > b0e9eb5ef022..dc0338a783f3 100644
> > --- a/include/linux/tpm.h
> > +++ b/include/linux/tpm.h
> > @@ -228,10 +228,11 @@ enum tpm2_timeouts {
> > TPM2_TIMEOUT_B = 4000,
> > TPM2_TIMEOUT_C = 200,
> > TPM2_TIMEOUT_D = 30,
> > +};
> > +
> > +enum tpm2_durations {
> > TPM2_DURATION_SHORT = 20,
> > - TPM2_DURATION_MEDIUM = 750,
> > TPM2_DURATION_LONG = 2000,
> > - TPM2_DURATION_LONG_LONG = 300000,
> > TPM2_DURATION_DEFAULT = 120000,
> > };
> >
> > --
> > 2.39.5
>
BR, Jarkko
^ permalink raw reply
* Re: [PATCH] keys: prevent slab cache merging for key_jar
From: Jarkko Sakkinen @ 2026-06-09 15:36 UTC (permalink / raw)
To: Mohammed EL Kadiri
Cc: Vlastimil Babka (SUSE), David Howells, Paul Moore, James Morris,
Serge E . Hallyn, Kees Cook, Vlastimil Babka, keyrings,
linux-security-module, linux-hardening, linux-kernel
In-Reply-To: <CAAMeuQTue5bkbhjYLjsRCnB9=KBifsPNQ=ZRVp+wCh2TiXE7Uw@mail.gmail.com>
On Tue, Jun 09, 2026 at 04:15:18PM +0100, Mohammed EL Kadiri wrote:
> Hi Jarkko, Vlastimil,
>
> I will send a v2 this evening with the commit message reworked. I will
> remove CVE references and frame it as hardening rather than an active
> vulnerability. I'll also drop the inaccurate skbuff_head_cache
> comparison that Vlastimil flagged.
>
> No need to strip it yourself, I'll have it out today.
Take your time :-) And thanks Vlastimil for the remark!
If I get the patch within this week, all is fine.
>
> Thanks,
> Mohammed
BR, Jarkko
^ permalink raw reply
* Re: [PATCH] keys: prevent slab cache merging for key_jar
From: Mohammed EL Kadiri @ 2026-06-09 15:15 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: Vlastimil Babka (SUSE), David Howells, Paul Moore, James Morris,
Serge E . Hallyn, Kees Cook, Vlastimil Babka, keyrings,
linux-security-module, linux-hardening, linux-kernel
In-Reply-To: <aigoZeoqygyBYCbR@kernel.org>
Hi Jarkko, Vlastimil,
I will send a v2 this evening with the commit message reworked. I will
remove CVE references and frame it as hardening rather than an active
vulnerability. I'll also drop the inaccurate skbuff_head_cache
comparison that Vlastimil flagged.
No need to strip it yourself, I'll have it out today.
Thanks,
Mohammed
^ permalink raw reply
* Re: [PATCH] keys: prevent slab cache merging for key_jar
From: Jarkko Sakkinen @ 2026-06-09 14:52 UTC (permalink / raw)
To: Vlastimil Babka (SUSE)
Cc: Mohammed EL Kadiri, David Howells, Paul Moore, James Morris,
Serge E . Hallyn, Kees Cook, Vlastimil Babka, keyrings,
linux-security-module, linux-hardening, linux-kernel
In-Reply-To: <85a686a6-7fcc-4a1d-8574-0fc7c2f84bc8@kernel.org>
On Mon, Jun 08, 2026 at 09:20:39AM +0200, Vlastimil Babka (SUSE) wrote:
> On 6/8/26 06:22, Jarkko Sakkinen wrote:
> > On Thu, Jun 04, 2026 at 01:50:34PM +0100, Mohammed EL Kadiri wrote:
> >> The key_jar slab cache holds struct key objects containing cryptographic
> >> keys, authentication tokens, and keyring linkage. This cache currently
> >> lacks merge prevention, allowing the SLUB allocator to merge it with
> >> other similarly-sized caches.
> >>
> >> On a default Ubuntu 6.17.0-23-generic system, key_jar has 5 aliases,
> >> meaning 5 unrelated object types share its slab pages. struct key is
> >> 224 bytes, placed in 256-byte slabs alongside biovec-16, maple_node,
> >> ip6_dst_cache, task_delay_info, and kmalloc-256 users.
> >>
> >> Cross-cache heap exploitation is a well-documented attack class
> >> (CVE-2022-29582, CVE-2022-2588, CVE-2021-22555) where slab cache
> >> merging enables type confusion between unrelated kernel objects. A
> >> use-after-free in any subsystem sharing slab pages with key_jar could
> >> allow an attacker to reclaim a freed slot as a struct key, or corrupt
> >> an existing key through a dangling pointer to a different type.
> >>
> >> Add SLAB_NO_MERGE to ensure key_jar receives dedicated slab pages,
> >> eliminating cross-cache attacks targeting struct key. The memory
> >> overhead is minimal: with 32 objects per slab page and typical key
> >> usage bounded by system keyring size, the cost of dedicated pages is
> >> negligible. There is zero performance impact on the allocation hot
> >> path.
> >>
> >> This follows the precedent set by skbuff_head_cache (net/core/skbuff.c)
> >> which uses SLAB_NO_MERGE for similar isolation requirements.
>
> I just realized this part is somewhat misleading, because it's done there
> for performance reasons, so I wouldn't say "similar".
Mohammed, could you at least send v2, which does not emphasize on
CVEs? It's better the use no_merge for sake of hardnening but some
risk is not same as vulnerability.
I'll replace the patch in my tree with updated once I get it.
Or I can strip off the parts myself, whatever goes...
>
> >
> > ~/work/kernel.org/jarkko/linux-tpmdd master*
> > ❯ git log --oneline -1 d0bf7d5759c1d89fb013aa41cca5832e00b9632a
> > d0bf7d5759c1 mm/slab: introduce kmem_cache flag SLAB_NO_MERGE
> >
> > ~/work/kernel.org/jarkko/linux-tpmdd master*
> > ❯ git describe --contains d0bf7d5759c1d89fb013aa41cca5832e00b9632a
> > v6.5-rc1~137^2^3~1
> >
> > So we could probably forward to stable's starting from v6.6+ if that
> > is necessary / makes sense?
>
> It won't hurt, but I doubt it's "necessary" per stable rules. But stable
> maintainers ignore those themselves anyway, so whatever.
>
> > It's not a bug fix but kind of still I think would be a change that
> > stable kernels are better off with it than without it.
> >
> > What do you think?
>
> Won't object.
Yeah, I agree, and yeah I think commit message goes over the top, while
the change is for better.
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v17 00/10] rust: add `Ownable` trait and `Owned` type
From: Miguel Ojeda @ 2026-06-09 14:11 UTC (permalink / raw)
To: Andreas Hindborg
Cc: Miguel Ojeda, Gary Guo, Björn Roy Baron, Benno Lossin,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman,
Dave Ertman, Ira Weiny, Leon Romanovsky, Paul Moore, Serge Hallyn,
Rafael J. Wysocki, David Airlie, Simona Vetter, Alexander Viro,
Christian Brauner, Jan Kara, Daniel Almeida, Viresh Kumar,
Nishanth Menon, Stephen Boyd, Bjorn Helgaas,
Krzysztof Wilczyński, Boqun Feng, Uladzislau Rezki,
Lorenzo Stoakes, Vlastimil Babka, Liam R. Howlett, Igor Korotin,
Pavel Tikhomirov, linux-kernel, rust-for-linux, linux-block,
linux-security-module, dri-devel, linux-fsdevel, linux-mm,
linux-pm, linux-pci, driver-core, Asahi Lina, Oliver Mangold,
Viresh Kumar
In-Reply-To: <20260604-unique-ref-v17-0-7b4c3d2930b9@kernel.org>
On Thu, Jun 4, 2026 at 10:13 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>
> rust: page: update formatting of `use` statements
> rust: aref: update formatting of use statements
Applied these to `rust-next` to get them out of the way already since
I have another similar one for `str` I am applying too -- thanks!
[ Picked from larger series and reworded. - Miguel ]
Cheers,
Miguel
^ permalink raw reply
* Re: [PATCH v6 2/4] security: ima: introduce IMA_INIT_LATE_SYNC option
From: Yeoreum Yun @ 2026-06-09 11:13 UTC (permalink / raw)
To: Mimi Zohar
Cc: linux-security-module, linux-kernel, linux-integrity, paul,
roberto.sassu, noodles, jarkko, sudeep.holla, jmorris, serge,
dmitry.kasatkin, eric.snowberg, jgg
In-Reply-To: <f135eab04611432040e4ba66cef2786424af8cc8.camel@linux.ibm.com>
On Mon, Jun 08, 2026 at 01:40:00PM -0400, Mimi Zohar wrote:
> On Fri, 2026-06-05 at 15:43 +0100, Yeoreum Yun wrote:
> > To generate the boot_aggregate log in the IMA subsystem with
> > TPM PCR values, the TPM driver must be built as built-in and
> > must be probed before the IMA subsystem is initialized.
> >
> > However, when the TPM device operates over the FF-A protocol using
> > the CRB interface, probing fails and returns -EPROBE_DEFER if
> > the tpm_crb_ffa device — an FF-A device that provides the communication
> > interface to the tpm_crb driver — has not yet been probed.
> >
> > To ensure the TPM device operating over the FF-A protocol with
> > the CRB interface is probed before IMA initialization,
> > the following conditions must be met:
> >
> > 1. The corresponding ffa_device must be registered,
> > which is done via ffa_init().
> >
> > 2. The tpm_crb_driver must successfully probe this device via
> > tpm_crb_ffa_init().
> >
> > 3. The tpm_crb driver using CRB over FF-A can then
> > be probed successfully. (See crb_acpi_add() and
> > tpm_crb_ffa_init() for reference.)
> >
> > Unfortunately, ffa_init(), tpm_crb_ffa_init(), and crb_acpi_driver_init()
> > are all registered with device_initcall, which means
> > crb_acpi_driver_init() may be invoked before ffa_init() and
> > tpm_crb_ffa_init() are completed.
> >
> > When this occurs, probing the TPM device is deferred.
> > However, the deferred probe can happen after the IMA subsystem
> > has already been initialized, since IMA initialization is performed
> > during late_initcall, and deferred_probe_initcall() is performed
> > at the same level.
> >
> > And the similar situation is reported on TPM devices attached on SPI
> > bus[0].
> >
> > To resolve this, introduce IMA_INIT_LATE_SYNC option to initialise
> > IMA at late_inicall_sync so that IMA is initialized with the TPM
> > device probed deferred.
> >
> > When this option is enabled, modules that access files in the
> > initramfs through usermode helper calls such as request_module()
> > during initcall must not be built-in. Otherwise, IMA may miss
> > measuring those files [1].
> >
> > Link: https://lore.kernel.org/all/aYXEepLhUouN5f99@earth.li/ [0]
> > Link: https://lore.kernel.org/all/2b3782398cc17ce9d355490a0c42ebce9120a9ae.camel@linux.ibm.com/ [1]
> > Suggested-by: Mimi Zohar <zohar@linux.ibm.com>
> > Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
> > Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
> > ---
> > security/integrity/ima/Kconfig | 10 ++++++++++
> > security/integrity/ima/ima_main.c | 4 ++++
> > 2 files changed, 14 insertions(+)
> >
> > diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
> > index 862fbee2b174..75f71401fba3 100644
> > --- a/security/integrity/ima/Kconfig
> > +++ b/security/integrity/ima/Kconfig
> > @@ -332,4 +332,14 @@ config IMA_KEXEC_EXTRA_MEMORY_KB
> > If set to the default value of 0, an extra half page of memory for those
> > additional measurements will be allocated.
> >
> > +config IMA_INIT_LATE_SYNC
> > + bool "Initialise IMA at late_initcall_sync"
> > + default n
> > + help
> > + This option initialises IMA at late_initcall_sync for platforms
> > + where TPM device probing is deferred.
> > + When this option is enabled, modules that access files in the
> > + initramfs through usermode helper calls such as request_module()
> > + during initcall must not be built-in. Otherwise, IMA may miss
> > + file measurements for them.
> > endif
>
> I fixed the merge conflict with the "ima: Exporting and deleting IMA measurement
> records from kernel memory" patch set. These patches are now queued in next-
> integrity-testing awaiting Paul's Ack.
Thanks!
[...]
--
Sincerely,
Yeoreum Yun
^ permalink raw reply
* Re: [PATCH] selftests/landlock: explicitly disable audit
From: Mickaël Salaün @ 2026-06-08 22:51 UTC (permalink / raw)
To: Maximilian Heyne
Cc: stable, Günther Noack, Shuah Khan, linux-security-module,
linux-kselftest, linux-kernel
In-Reply-To: <20260529-welsh-nagoya-b4d9ca60@mheyne-amazon>
Thanks for this patch. I merged a few fixes and I'd be interested to
know if this one fix the issue you spotted:
https://git.kernel.org/pub/scm/linux/kernel/git/mic/linux.git/commit/?h=next&id=d8dfb4c7faa87c3e41a8678f38f136c2c7c036fa
On Fri, May 29, 2026 at 08:03:41PM +0000, Maximilian Heyne wrote:
> I'm seeing sporadic selftest failures, such as
>
> # RUN scoped_audit.connect_to_child ...
> # scoped_abstract_unix_test.c:314:connect_to_child:Expected 0 (0) == records.access (8)
> # connect_to_child: Test failed
> # FAIL scoped_audit.connect_to_child
> not ok 19 scoped_audit.connect_to_child
>
> This seems similar to what commit 3647a4977fb73d ("selftests/landlock:
> Drain stale audit records on init") tried to fix. However, the added
> drain loop is not effective. When setting the AUDIT_STATUS_PID, the
> kauditd_thread is woken up starting to send messages from the hold queue
> to the netlink. Depending on scheduling of this kthread not all messages
> might be send via the netlink in the 1 us interval.
>
> Therefore, instead of trying to drain the queue, let's just disable
> audit when running non-audit tests or more precisely disable it after
> audit-tests. This way we won't generate any new audit message that could
> interfere with the other tests.
>
> The comment saying that on process exit audit will be disabled is wrong.
> The closed file descriptor just causes an auditd_reset(), not a
> disablement. So future messages will be queued in the hold queue.
>
> Cc: stable@vger.kernel.org
> Fixes: 6a500b22971c ("selftests/landlock: Add tests for audit flags and domain IDs")
> Signed-off-by: Maximilian Heyne <mheyne@amazon.de>
> ---
>
> I've seen the failures on the 6.18 kernels but haven't tested on latest
> upstream. However, I still think this is an issue.
>
> ---
> tools/testing/selftests/landlock/audit.h | 13 +++++--------
> 1 file changed, 5 insertions(+), 8 deletions(-)
>
> diff --git a/tools/testing/selftests/landlock/audit.h b/tools/testing/selftests/landlock/audit.h
> index 834005b2b0f09..7842330875f53 100644
> --- a/tools/testing/selftests/landlock/audit.h
> +++ b/tools/testing/selftests/landlock/audit.h
> @@ -494,10 +494,9 @@ static int audit_init_filter_exe(struct audit_filter *filter, const char *path)
> static int audit_cleanup(int audit_fd, struct audit_filter *filter)
audit_cleanup() should be called for audit_exec tests too.
> {
> struct audit_filter new_filter;
> + int err;
>
> if (audit_fd < 0 || !filter) {
> - int err;
> -
> /*
> * Simulates audit_init_with_exe_filter() when called from
> * FIXTURE_TEARDOWN_PARENT().
> @@ -518,12 +517,10 @@ static int audit_cleanup(int audit_fd, struct audit_filter *filter)
> audit_filter_exe(audit_fd, filter, AUDIT_DEL_RULE);
> audit_filter_drop(audit_fd, AUDIT_DEL_RULE);
>
> - /*
> - * Because audit_cleanup() might not be called by the test auditd
> - * process, it might not be possible to explicitly set it. Anyway,
> - * AUDIT_STATUS_ENABLED will implicitly be set to 0 when the auditd
> - * process will exit.
> - */
Please add a comment that explains that the audit state is not restored
but just disabled.
> + err = audit_set_status(audit_fd, AUDIT_STATUS_ENABLED, 0);
> + if (err)
> + return err;
> +
> return close(audit_fd);
FDs should always be closed.
> }
>
> --
> 2.50.1
>
>
>
>
> Amazon Web Services Development Center Germany GmbH
> Tamara-Danz-Str. 13
> 10243 Berlin
> Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
> Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
> Sitz: Berlin
> Ust-ID: DE 365 538 597
>
>
^ permalink raw reply
* Re: [PATCH v10 1/9] landlock: Add a place for flags to layer rules
From: Mickaël Salaün @ 2026-06-08 22:40 UTC (permalink / raw)
To: Tingmao Wang
Cc: Günther Noack, Justin Suess, Jan Kara, Abhinav Saxena,
linux-security-module
In-Reply-To: <676e38f1c2d3f6445bdcd45be043e514f6509a3a.1780272022.git.m@maowtm.org>
On Mon, Jun 01, 2026 at 01:00:35AM +0100, Tingmao Wang wrote:
> To avoid unnecessarily increasing the size of struct landlock_layer, we
> make the layer level a u8 and use the space to store the flags struct.
>
> struct layer_access_masks is renamed to struct layer_masks, and a new
> field is added to track whether a quiet flag rule is seen for each
> layer. Through use of bitfields, this does not increase the size of the
> struct.
>
> Cc: Justin Suess <utilityemal77@gmail.com>
> Assisted-by: GitHub Copilot:claude-opus-4.7 copilot-review
> Signed-off-by: Tingmao Wang <m@maowtm.org>
> Co-developed-by: Justin Suess <utilityemal77@gmail.com>
> Signed-off-by: Justin Suess <utilityemal77@gmail.com>
> Tested-by: Justin Suess <utilityemal77@gmail.com>
> ---
>
> Changes in v10:
> - Doc for struct layer_mask members
> - clang-format header file changes
> - Add Tested-by for Justin Suess
>
> Changes in v9:
> - Move a hunk from patch 2 to here
> - Fix comment and format
> - Renamed struct layer_access_masks to struct layer_masks, and moved the
> content of struct collected_rule_flags into this struct, getting rid
> of the extra struct collected_rule_flags and function parameters.
> This is following a discussion in [3]. The flag is now initialized in
> landlock_init_layer_masks as false.
> - Thus also removed now unnecessary layer_mask_t
>
> Changes in v8:
> - Rebase on top of mic/next
> - Add Co-developed-by: Justin Suess for handling this rebase initially
> - layer_mask_t was removed in [1] but we still need it for the
> collected_rule_flags. Rather than using raw u16, I've chosen to
> re-define it back in ruleset.h (it was in access.h).
>
> Changes in v7:
> - Take rule_flags separately from landlock_request in
> is_access_to_paths_allowed to avoid writing to the landlock_request
> variable if CONFIG_AUDIT is disabled (to enable compiler elision).
> - Due to the above change, we don't need rule_flags in landlock_request in
> this commit anymore (will be added later).
>
> Changes in v6:
> - Rebased to include the revised disconnected directory handling changes
> (without the "reverting" behaviour)
>
> Changes in v5:
> - Move rule_flags into landlock_request. This lets us get rid of the
> extra parameters to is_access_to_paths_allowed (and later on,
> landlock_log_denial), and thus less code changes.
>
> Changes in v3:
> - Comment changes, move local variables, simplify if branch
>
> Changes in v2:
> - Comment changes
> - Rebased to include disconnected directory handling changes on mic/next
> and add backing up of collected_rule_flags.
>
> [1]: https://lore.kernel.org/all/20260125195853.109967-1-gnoack3000@gmail.com/
> [2]: https://lore.kernel.org/all/20251221194301.247484-1-utilityemal77@gmail.com/
> [3]: https://lore.kernel.org/all/20260524.eFiz4hahrami@digikod.net/
>
> security/landlock/access.h | 39 +++++++---
> security/landlock/audit.c | 20 ++---
> security/landlock/audit.h | 2 +-
> security/landlock/domain.c | 19 ++---
> security/landlock/domain.h | 2 +-
> security/landlock/fs.c | 147 +++++++++++++++++++-----------------
> security/landlock/limits.h | 3 +
> security/landlock/net.c | 2 +-
> security/landlock/ruleset.c | 33 +++++---
> security/landlock/ruleset.h | 17 ++++-
> 10 files changed, 173 insertions(+), 111 deletions(-)
>
> diff --git a/security/landlock/access.h b/security/landlock/access.h
> index c19d5bc13944..42d8b5134358 100644
> --- a/security/landlock/access.h
> +++ b/security/landlock/access.h
> @@ -62,18 +62,39 @@ static_assert(sizeof(typeof_member(union access_masks_all, masks)) ==
> sizeof(typeof_member(union access_masks_all, all)));
>
> /**
> - * struct layer_access_masks - A boolean matrix of layers and access rights
> - *
> - * This has a bit for each combination of layer numbers and access rights.
> - * During access checks, it is used to represent the access rights for each
> - * layer which still need to be fulfilled. When all bits are 0, the access
> - * request is considered to be fulfilled.
> + * struct layer_mask - The unfulfilled access rights and rule flags for
This struct could be used to store "fulfilled" access rights too. The
previous description is more accurate. Please keep most of the previous
description too and adjust as needed.
> + * a layer.
> + */
> +struct layer_mask {
> + /**
> + * @access: During access checks, this is used to represent the access
> + * rights for each layer which still need to be fulfilled. When all
> + * bits are 0, the access request is allowed by this layer.
> + */
> + access_mask_t access : LANDLOCK_NUM_ACCESS_MAX;
> +#ifdef CONFIG_AUDIT
> + /**
> + * @quiet: Whether we have encountered a rule with the quiet flag for
> + * this layer. Used to control audit logging.
> + */
> + bool quiet : 1;
According to compiler specifications, to make sure the struct is the
size of access_masks_t, we need:
access_masks_t quiet : 1;
> +#endif /* CONFIG_AUDIT */
> +};
Like for struct access_masks, please use
__packed __aligned(sizeof(access_mask_t)) attributes.
> +
> +/*
> + * Make sure that we don't increase the size of struct layer_mask when
> + * storing rule flags.
> + */
> +static_assert(sizeof(struct layer_mask) == sizeof(access_mask_t));
> +
> +/**
> + * struct layer_masks - An array of struct layer_mask, one per layer.
> */
> -struct layer_access_masks {
> +struct layer_masks {
> /**
> - * @access: The unfulfilled access rights for each layer.
> + * @layers: The unfulfilled access rights for each layer.
> */
> - access_mask_t access[LANDLOCK_MAX_NUM_LAYERS];
> + struct layer_mask layers[LANDLOCK_MAX_NUM_LAYERS];
> };
>
> /*
> diff --git a/security/landlock/audit.c b/security/landlock/audit.c
> index 851647197a01..8c56f7f6467a 100644
> --- a/security/landlock/audit.c
> +++ b/security/landlock/audit.c
> @@ -187,11 +187,11 @@ static void test_get_hierarchy(struct kunit *const test)
> /* Get the youngest layer that denied the access_request. */
> static size_t get_denied_layer(const struct landlock_ruleset *const domain,
> access_mask_t *const access_request,
> - const struct layer_access_masks *masks)
> + const struct layer_masks *masks)
> {
> - for (ssize_t i = ARRAY_SIZE(masks->access) - 1; i >= 0; i--) {
> - if (masks->access[i] & *access_request) {
> - *access_request &= masks->access[i];
> + for (ssize_t i = ARRAY_SIZE(masks->layers) - 1; i >= 0; i--) {
> + if (masks->layers[i].access & *access_request) {
> + *access_request &= masks->layers[i].access;
> return i;
> }
> }
> @@ -208,12 +208,12 @@ static void test_get_denied_layer(struct kunit *const test)
> const struct landlock_ruleset dom = {
> .num_layers = 5,
> };
> - const struct layer_access_masks masks = {
> - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE |
> - LANDLOCK_ACCESS_FS_READ_DIR,
> - .access[1] = LANDLOCK_ACCESS_FS_READ_FILE |
> - LANDLOCK_ACCESS_FS_READ_DIR,
> - .access[2] = LANDLOCK_ACCESS_FS_REMOVE_DIR,
> + const struct layer_masks masks = {
> + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE |
> + LANDLOCK_ACCESS_FS_READ_DIR,
> + .layers[1].access = LANDLOCK_ACCESS_FS_READ_FILE |
> + LANDLOCK_ACCESS_FS_READ_DIR,
> + .layers[2].access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
> };
> access_mask_t access;
>
> diff --git a/security/landlock/audit.h b/security/landlock/audit.h
> index 56778331b58c..b85d752273ac 100644
> --- a/security/landlock/audit.h
> +++ b/security/landlock/audit.h
> @@ -43,7 +43,7 @@ struct landlock_request {
> access_mask_t access;
>
> /* Required fields for requests with layer masks. */
> - const struct layer_access_masks *layer_masks;
> + const struct layer_masks *layer_masks;
>
> /* Required fields for requests with deny masks. */
> const access_mask_t all_existing_optional_access;
> diff --git a/security/landlock/domain.c b/security/landlock/domain.c
> index 5dd06f7c2312..d1a4d8b33ee1 100644
> --- a/security/landlock/domain.c
> +++ b/security/landlock/domain.c
> @@ -184,7 +184,7 @@ static void test_get_layer_deny_mask(struct kunit *const test)
> deny_masks_t
> landlock_get_deny_masks(const access_mask_t all_existing_optional_access,
> const access_mask_t optional_access,
> - const struct layer_access_masks *const masks)
> + const struct layer_masks *const masks)
> {
> const unsigned long access_opt = optional_access;
> unsigned long access_bit;
> @@ -201,8 +201,9 @@ landlock_get_deny_masks(const access_mask_t all_existing_optional_access,
> if (WARN_ON_ONCE(!access_opt))
> return 0;
>
> - for (ssize_t i = ARRAY_SIZE(masks->access) - 1; i >= 0; i--) {
> - const access_mask_t denied = masks->access[i] & optional_access;
> + for (ssize_t i = ARRAY_SIZE(masks->layers) - 1; i >= 0; i--) {
> + const access_mask_t denied = masks->layers[i].access &
> + optional_access;
> const unsigned long newly_denied = denied & ~all_denied;
>
> if (!newly_denied)
> @@ -222,12 +223,12 @@ landlock_get_deny_masks(const access_mask_t all_existing_optional_access,
>
> static void test_landlock_get_deny_masks(struct kunit *const test)
> {
> - const struct layer_access_masks layers1 = {
> - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE |
> - LANDLOCK_ACCESS_FS_IOCTL_DEV,
> - .access[1] = LANDLOCK_ACCESS_FS_TRUNCATE,
> - .access[2] = LANDLOCK_ACCESS_FS_IOCTL_DEV,
> - .access[9] = LANDLOCK_ACCESS_FS_EXECUTE,
> + const struct layer_masks layers1 = {
> + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE |
> + LANDLOCK_ACCESS_FS_IOCTL_DEV,
> + .layers[1].access = LANDLOCK_ACCESS_FS_TRUNCATE,
> + .layers[2].access = LANDLOCK_ACCESS_FS_IOCTL_DEV,
> + .layers[9].access = LANDLOCK_ACCESS_FS_EXECUTE,
> };
>
> KUNIT_EXPECT_EQ(test, 0x1,
> diff --git a/security/landlock/domain.h b/security/landlock/domain.h
> index 35cac8f6daee..af100a8cd939 100644
> --- a/security/landlock/domain.h
> +++ b/security/landlock/domain.h
> @@ -119,7 +119,7 @@ struct landlock_hierarchy {
> deny_masks_t
> landlock_get_deny_masks(const access_mask_t all_existing_optional_access,
> const access_mask_t optional_access,
> - const struct layer_access_masks *const masks);
> + const struct layer_masks *const masks);
>
> int landlock_init_hierarchy_log(struct landlock_hierarchy *const hierarchy);
>
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index edaa52572cbd..f7c1bc64de20 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -406,15 +406,15 @@ static const struct access_masks any_fs = {
> * src_parent would result in having the same or fewer access rights if it were
> * moved under new_parent.
> */
> -static bool may_refer(const struct layer_access_masks *const src_parent,
> - const struct layer_access_masks *const src_child,
> - const struct layer_access_masks *const new_parent,
> +static bool may_refer(const struct layer_masks *const src_parent,
> + const struct layer_masks *const src_child,
> + const struct layer_masks *const new_parent,
> const bool child_is_dir)
> {
> - for (size_t i = 0; i < ARRAY_SIZE(new_parent->access); i++) {
> - access_mask_t child_access = src_parent->access[i] &
> - src_child->access[i];
> - access_mask_t parent_access = new_parent->access[i];
> + for (size_t i = 0; i < ARRAY_SIZE(new_parent->layers); i++) {
> + access_mask_t child_access = src_parent->layers[i].access &
> + src_child->layers[i].access;
> + access_mask_t parent_access = new_parent->layers[i].access;
>
> if (!child_is_dir) {
> child_access &= ACCESS_FILE;
> @@ -436,11 +436,11 @@ static bool may_refer(const struct layer_access_masks *const src_parent,
> * that child2 may be used from parent2 to parent1 without increasing its access
> * rights), false otherwise.
> */
> -static bool no_more_access(const struct layer_access_masks *const parent1,
> - const struct layer_access_masks *const child1,
> +static bool no_more_access(const struct layer_masks *const parent1,
> + const struct layer_masks *const child1,
> const bool child1_is_dir,
> - const struct layer_access_masks *const parent2,
> - const struct layer_access_masks *const child2,
> + const struct layer_masks *const parent2,
> + const struct layer_masks *const child2,
> const bool child2_is_dir)
> {
> if (!may_refer(parent1, child1, parent2, child1_is_dir))
> @@ -459,25 +459,25 @@ static bool no_more_access(const struct layer_access_masks *const parent1,
>
> static void test_no_more_access(struct kunit *const test)
> {
> - const struct layer_access_masks rx0 = {
> - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE |
> - LANDLOCK_ACCESS_FS_READ_FILE,
> + const struct layer_masks rx0 = {
> + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE |
> + LANDLOCK_ACCESS_FS_READ_FILE,
> };
> - const struct layer_access_masks mx0 = {
> - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE |
> - LANDLOCK_ACCESS_FS_MAKE_REG,
> + const struct layer_masks mx0 = {
> + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE |
> + LANDLOCK_ACCESS_FS_MAKE_REG,
> };
> - const struct layer_access_masks x0 = {
> - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE,
> + const struct layer_masks x0 = {
> + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE,
> };
> - const struct layer_access_masks x1 = {
> - .access[1] = LANDLOCK_ACCESS_FS_EXECUTE,
> + const struct layer_masks x1 = {
> + .layers[1].access = LANDLOCK_ACCESS_FS_EXECUTE,
> };
> - const struct layer_access_masks x01 = {
> - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE,
> - .access[1] = LANDLOCK_ACCESS_FS_EXECUTE,
> + const struct layer_masks x01 = {
> + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE,
> + .layers[1].access = LANDLOCK_ACCESS_FS_EXECUTE,
> };
> - const struct layer_access_masks allows_all = {};
> + const struct layer_masks allows_all = {};
>
> /* Checks without restriction. */
> NMA_TRUE(&x0, &allows_all, false, &allows_all, NULL, false);
> @@ -565,9 +565,13 @@ static void test_no_more_access(struct kunit *const test)
> #undef NMA_TRUE
> #undef NMA_FALSE
>
> -static bool is_layer_masks_allowed(const struct layer_access_masks *masks)
> +static bool is_layer_masks_allowed(const struct layer_masks *masks)
> {
> - return mem_is_zero(&masks->access, sizeof(masks->access));
> + for (size_t i = 0; i < ARRAY_SIZE(masks->layers); i++) {
> + if (masks->layers[i].access)
> + return false;
> + }
> + return true;
> }
>
> /*
> @@ -576,16 +580,16 @@ static bool is_layer_masks_allowed(const struct layer_access_masks *masks)
> * Returns true if the request is allowed, false otherwise.
> */
> static bool scope_to_request(const access_mask_t access_request,
> - struct layer_access_masks *masks)
> + struct layer_masks *masks)
> {
> bool saw_unfulfilled_access = false;
>
> if (WARN_ON_ONCE(!masks))
> return true;
>
> - for (size_t i = 0; i < ARRAY_SIZE(masks->access); i++) {
> - masks->access[i] &= access_request;
> - if (masks->access[i])
> + for (size_t i = 0; i < ARRAY_SIZE(masks->layers); i++) {
> + masks->layers[i].access &= access_request;
> + if (masks->layers[i].access)
> saw_unfulfilled_access = true;
> }
> return !saw_unfulfilled_access;
> @@ -596,41 +600,46 @@ static bool scope_to_request(const access_mask_t access_request,
> static void test_scope_to_request_with_exec_none(struct kunit *const test)
> {
> /* Allows everything. */
> - struct layer_access_masks masks = {};
> + struct layer_masks masks = {};
>
> /* Checks and scopes with execute. */
> KUNIT_EXPECT_TRUE(test,
> scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE, &masks));
> - KUNIT_EXPECT_EQ(test, 0, masks.access[0]);
> + KUNIT_EXPECT_EQ(test, 0, (access_mask_t)masks.layers[0].access);
> }
>
> static void test_scope_to_request_with_exec_some(struct kunit *const test)
> {
> /* Denies execute and write. */
> - struct layer_access_masks masks = {
> - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE,
> - .access[1] = LANDLOCK_ACCESS_FS_WRITE_FILE,
> + struct layer_masks masks = {
> + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE,
> + .layers[1].access = LANDLOCK_ACCESS_FS_WRITE_FILE,
> };
>
> /* Checks and scopes with execute. */
> KUNIT_EXPECT_FALSE(test, scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE,
> &masks));
> - KUNIT_EXPECT_EQ(test, LANDLOCK_ACCESS_FS_EXECUTE, masks.access[0]);
> - KUNIT_EXPECT_EQ(test, 0, masks.access[1]);
> + /*
> + * These casts to access_mask_t are needed because typeof(), used in
> + * KUNIT_EXPECT_EQ(), does not work on bitfields.
> + */
> + KUNIT_EXPECT_EQ(test, LANDLOCK_ACCESS_FS_EXECUTE,
> + (access_mask_t)masks.layers[0].access);
> + KUNIT_EXPECT_EQ(test, 0, (access_mask_t)masks.layers[1].access);
> }
>
> static void test_scope_to_request_without_access(struct kunit *const test)
> {
> /* Denies execute and write. */
> - struct layer_access_masks masks = {
> - .access[0] = LANDLOCK_ACCESS_FS_EXECUTE,
> - .access[1] = LANDLOCK_ACCESS_FS_WRITE_FILE,
> + struct layer_masks masks = {
> + .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE,
> + .layers[1].access = LANDLOCK_ACCESS_FS_WRITE_FILE,
> };
>
> /* Checks and scopes without access request. */
> KUNIT_EXPECT_TRUE(test, scope_to_request(0, &masks));
> - KUNIT_EXPECT_EQ(test, 0, masks.access[0]);
> - KUNIT_EXPECT_EQ(test, 0, masks.access[1]);
> + KUNIT_EXPECT_EQ(test, 0, (access_mask_t)masks.layers[0].access);
> + KUNIT_EXPECT_EQ(test, 0, (access_mask_t)masks.layers[1].access);
> }
>
> #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
> @@ -639,15 +648,15 @@ static void test_scope_to_request_without_access(struct kunit *const test)
> * Returns true if there is at least one access right different than
> * LANDLOCK_ACCESS_FS_REFER.
> */
> -static bool is_eacces(const struct layer_access_masks *masks,
> +static bool is_eacces(const struct layer_masks *masks,
> const access_mask_t access_request)
> {
> if (!masks)
> return false;
>
> - for (size_t i = 0; i < ARRAY_SIZE(masks->access); i++) {
> + for (size_t i = 0; i < ARRAY_SIZE(masks->layers); i++) {
> /* LANDLOCK_ACCESS_FS_REFER alone must return -EXDEV. */
> - if (masks->access[i] & access_request &
> + if (masks->layers[i].access & access_request &
> ~LANDLOCK_ACCESS_FS_REFER)
> return true;
> }
> @@ -661,7 +670,7 @@ static bool is_eacces(const struct layer_access_masks *masks,
>
> static void test_is_eacces_with_none(struct kunit *const test)
> {
> - const struct layer_access_masks masks = {};
> + const struct layer_masks masks = {};
>
> IE_FALSE(&masks, 0);
> IE_FALSE(&masks, LANDLOCK_ACCESS_FS_REFER);
> @@ -671,8 +680,8 @@ static void test_is_eacces_with_none(struct kunit *const test)
>
> static void test_is_eacces_with_refer(struct kunit *const test)
> {
> - const struct layer_access_masks masks = {
> - .access[0] = LANDLOCK_ACCESS_FS_REFER,
> + const struct layer_masks masks = {
> + .layers[0].access = LANDLOCK_ACCESS_FS_REFER,
> };
>
> IE_FALSE(&masks, 0);
> @@ -683,8 +692,8 @@ static void test_is_eacces_with_refer(struct kunit *const test)
>
> static void test_is_eacces_with_write(struct kunit *const test)
> {
> - const struct layer_access_masks masks = {
> - .access[0] = LANDLOCK_ACCESS_FS_WRITE_FILE,
> + const struct layer_masks masks = {
> + .layers[0].access = LANDLOCK_ACCESS_FS_WRITE_FILE,
> };
>
> IE_FALSE(&masks, 0);
> @@ -743,11 +752,11 @@ static bool
> is_access_to_paths_allowed(const struct landlock_ruleset *const domain,
> const struct path *const path,
> const access_mask_t access_request_parent1,
> - struct layer_access_masks *layer_masks_parent1,
> + struct layer_masks *layer_masks_parent1,
> struct landlock_request *const log_request_parent1,
> struct dentry *const dentry_child1,
> const access_mask_t access_request_parent2,
> - struct layer_access_masks *layer_masks_parent2,
> + struct layer_masks *layer_masks_parent2,
> struct landlock_request *const log_request_parent2,
> struct dentry *const dentry_child2)
> {
> @@ -755,9 +764,9 @@ is_access_to_paths_allowed(const struct landlock_ruleset *const domain,
> child1_is_directory = true, child2_is_directory = true;
> struct path walker_path;
> access_mask_t access_masked_parent1, access_masked_parent2;
> - struct layer_access_masks _layer_masks_child1, _layer_masks_child2;
> - struct layer_access_masks *layer_masks_child1 = NULL,
> - *layer_masks_child2 = NULL;
> + struct layer_masks _layer_masks_child1, _layer_masks_child2;
> + struct layer_masks *layer_masks_child1 = NULL,
> + *layer_masks_child2 = NULL;
>
> if (!access_request_parent1 && !access_request_parent2)
> return true;
> @@ -797,6 +806,10 @@ is_access_to_paths_allowed(const struct landlock_ruleset *const domain,
> }
>
> if (unlikely(dentry_child1)) {
> + /*
> + * Get the layer masks for the child dentries for use by domain
> + * check later.
> + */
> if (landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
> &_layer_masks_child1,
> LANDLOCK_KEY_INODE))
> @@ -952,7 +965,7 @@ static int current_check_access_path(const struct path *const path,
> };
> const struct landlock_cred_security *const subject =
> landlock_get_applicable_subject(current_cred(), masks, NULL);
> - struct layer_access_masks layer_masks;
> + struct layer_masks layer_masks;
> struct landlock_request request = {};
>
> if (!subject)
> @@ -1029,7 +1042,7 @@ static access_mask_t maybe_remove(const struct dentry *const dentry)
> static bool collect_domain_accesses(const struct landlock_ruleset *const domain,
> const struct dentry *const mnt_root,
> struct dentry *dir,
> - struct layer_access_masks *layer_masks_dom)
> + struct layer_masks *layer_masks_dom)
> {
> bool ret = false;
>
> @@ -1135,8 +1148,7 @@ static int current_check_refer_path(struct dentry *const old_dentry,
> access_mask_t access_request_parent1, access_request_parent2;
> struct path mnt_dir;
> struct dentry *old_parent;
> - struct layer_access_masks layer_masks_parent1 = {},
> - layer_masks_parent2 = {};
> + struct layer_masks layer_masks_parent1 = {}, layer_masks_parent2 = {};
> struct landlock_request request1 = {}, request2 = {};
>
> if (!subject)
> @@ -1202,7 +1214,6 @@ static int current_check_refer_path(struct dentry *const old_dentry,
> allow_parent2 = collect_domain_accesses(subject->domain, mnt_dir.dentry,
> new_dir->dentry,
> &layer_masks_parent2);
> -
> if (allow_parent1 && allow_parent2)
> return 0;
>
> @@ -1580,7 +1591,7 @@ static int hook_path_truncate(const struct path *const path)
> */
> static void unmask_scoped_access(const struct landlock_ruleset *const client,
> const struct landlock_ruleset *const server,
> - struct layer_access_masks *const masks,
> + struct layer_masks *const masks,
> const access_mask_t access)
> {
> int client_layer, server_layer;
> @@ -1621,9 +1632,9 @@ static void unmask_scoped_access(const struct landlock_ruleset *const client,
> server_walker = server_walker->parent;
>
> for (; client_layer >= 0; client_layer--) {
> - if (masks->access[client_layer] & access &&
> + if (masks->layers[client_layer].access & access &&
> client_walker == server_walker)
> - masks->access[client_layer] &= ~access;
> + masks->layers[client_layer].access &= ~access;
>
> client_walker = client_walker->parent;
> server_walker = server_walker->parent;
> @@ -1635,7 +1646,7 @@ static int hook_unix_find(const struct path *const path, struct sock *other,
> {
> const struct landlock_ruleset *dom_other;
> const struct landlock_cred_security *subject;
> - struct layer_access_masks layer_masks;
> + struct layer_masks layer_masks;
> struct landlock_request request = {};
> static const struct access_masks fs_resolve_unix = {
> .fs = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> @@ -1739,7 +1750,7 @@ static bool is_device(const struct file *const file)
>
> static int hook_file_open(struct file *const file)
> {
> - struct layer_access_masks layer_masks = {};
> + struct layer_masks layer_masks = {};
> access_mask_t open_access_request, full_access_request, allowed_access,
> optional_access;
> const struct landlock_cred_security *const subject =
> @@ -1780,8 +1791,8 @@ static int hook_file_open(struct file *const file)
> * are still unfulfilled in any of the layers.
> */
> allowed_access = full_access_request;
> - for (size_t i = 0; i < ARRAY_SIZE(layer_masks.access); i++)
> - allowed_access &= ~layer_masks.access[i];
> + for (size_t i = 0; i < ARRAY_SIZE(layer_masks.layers); i++)
> + allowed_access &= ~layer_masks.layers[i].access;
> }
>
> /*
> diff --git a/security/landlock/limits.h b/security/landlock/limits.h
> index a4d908b240a2..08d5f2f6d321 100644
> --- a/security/landlock/limits.h
> +++ b/security/landlock/limits.h
> @@ -31,6 +31,9 @@
> #define LANDLOCK_MASK_SCOPE ((LANDLOCK_LAST_SCOPE << 1) - 1)
> #define LANDLOCK_NUM_SCOPE __const_hweight64(LANDLOCK_MASK_SCOPE)
>
> +#define LANDLOCK_NUM_ACCESS_MAX \
> + MAX(MAX(LANDLOCK_NUM_ACCESS_FS, LANDLOCK_NUM_ACCESS_NET), LANDLOCK_NUM_SCOPE)
> +
> #define LANDLOCK_LAST_RESTRICT_SELF LANDLOCK_RESTRICT_SELF_TSYNC
> #define LANDLOCK_MASK_RESTRICT_SELF ((LANDLOCK_LAST_RESTRICT_SELF << 1) - 1)
>
> diff --git a/security/landlock/net.c b/security/landlock/net.c
> index db2046a89a9a..981a362c24db 100644
> --- a/security/landlock/net.c
> +++ b/security/landlock/net.c
> @@ -48,7 +48,7 @@ static int current_check_access_socket(struct socket *const sock,
> bool connecting)
> {
> __be16 port;
> - struct layer_access_masks layer_masks = {};
> + struct layer_masks layer_masks = {};
> const struct landlock_rule *rule;
> struct landlock_id id = {
> .type = LANDLOCK_KEY_NET_PORT,
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index 181df7736bb9..91948e406e69 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -628,7 +628,7 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset,
> * remaining unfulfilled access rights and masks has no leftover set bits).
> */
> bool landlock_unmask_layers(const struct landlock_rule *const rule,
> - struct layer_access_masks *masks)
> + struct layer_masks *masks)
> {
> if (!masks)
> return true;
> @@ -649,11 +649,17 @@ bool landlock_unmask_layers(const struct landlock_rule *const rule,
> const struct landlock_layer *const layer = &rule->layers[i];
>
> /* Clear the bits where the layer in the rule grants access. */
> - masks->access[layer->level - 1] &= ~layer->access;
> + masks->layers[layer->level - 1].access &= ~layer->access;
> +
> +#ifdef CONFIG_AUDIT
> + /* Collect rule flags for each layer. */
> + if (layer->flags.quiet)
> + masks->layers[layer->level - 1].quiet = true;
> +#endif /* CONFIG_AUDIT */
> }
>
> - for (size_t i = 0; i < ARRAY_SIZE(masks->access); i++) {
> - if (masks->access[i])
> + for (size_t i = 0; i < ARRAY_SIZE(masks->layers); i++) {
> + if (masks->layers[i].access)
> return false;
> }
> return true;
> @@ -668,6 +674,7 @@ get_access_mask_t(const struct landlock_ruleset *const ruleset,
> *
> * Populates @masks such that for each access right in @access_request,
> * the bits for all the layers are set where this access right is handled.
> + * Rule flags are also zeroed.
> *
> * @domain: The domain that defines the current restrictions.
> * @access_request: The requested access rights to check.
> @@ -680,7 +687,7 @@ get_access_mask_t(const struct landlock_ruleset *const ruleset,
> access_mask_t
> landlock_init_layer_masks(const struct landlock_ruleset *const domain,
> const access_mask_t access_request,
> - struct layer_access_masks *const masks,
> + struct layer_masks *const masks,
> const enum landlock_key_type key_type)
> {
> access_mask_t handled_accesses = 0;
> @@ -709,11 +716,19 @@ landlock_init_layer_masks(const struct landlock_ruleset *const domain,
> for (size_t i = 0; i < domain->num_layers; i++) {
> const access_mask_t handled = get_access_mask(domain, i);
>
> - masks->access[i] = access_request & handled;
> - handled_accesses |= masks->access[i];
> + masks->layers[i].access = access_request & handled;
> + handled_accesses |= masks->layers[i].access;
> +#ifdef CONFIG_AUDIT
> + masks->layers[i].quiet = false;
> +#endif /* CONFIG_AUDIT */
> + }
> + for (size_t i = domain->num_layers; i < ARRAY_SIZE(masks->layers);
> + i++) {
> + masks->layers[i].access = 0;
> +#ifdef CONFIG_AUDIT
> + masks->layers[i].quiet = false;
> +#endif /* CONFIG_AUDIT */
> }
> - for (size_t i = domain->num_layers; i < ARRAY_SIZE(masks->access); i++)
> - masks->access[i] = 0;
>
> return handled_accesses;
> }
> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index 889f4b30301a..f80ca487d125 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -29,7 +29,18 @@ struct landlock_layer {
> /**
> * @level: Position of this layer in the layer stack. Starts from 1.
> */
> - u16 level;
> + u8 level;
> + /**
> + * @flags: Bitfield for special flags attached to this rule.
> + */
> + struct {
> + /**
> + * @quiet: Suppresses denial audit logs for the object covered by
> + * this rule in this domain. For filesystem rules, this inherits
> + * down the file hierarchy.
> + */
> + bool quiet : 1;
In struct, we should use architecture-independent sizes, so `u8 quiet : 1;`
seems more appropriate.
> + } flags;
> /**
> * @access: Bitfield of allowed actions on the kernel object. They are
> * relative to the object type (e.g. %LANDLOCK_ACTION_FS_READ).
> @@ -302,12 +313,12 @@ landlock_get_scope_mask(const struct landlock_ruleset *const ruleset,
> }
>
> bool landlock_unmask_layers(const struct landlock_rule *const rule,
> - struct layer_access_masks *masks);
> + struct layer_masks *masks);
>
> access_mask_t
> landlock_init_layer_masks(const struct landlock_ruleset *const domain,
> const access_mask_t access_request,
> - struct layer_access_masks *masks,
> + struct layer_masks *masks,
> const enum landlock_key_type key_type);
>
> #endif /* _SECURITY_LANDLOCK_RULESET_H */
> --
> 2.54.0
>
^ permalink raw reply
* Re: [PATCH v10 3/9] landlock: Suppress logging when quiet flag is present
From: Mickaël Salaün @ 2026-06-08 22:41 UTC (permalink / raw)
To: Tingmao Wang
Cc: Günther Noack, Justin Suess, Jan Kara, Abhinav Saxena,
linux-security-module
In-Reply-To: <d208c793ac5d5f45bd1bbf87f57d9cc3ab47d6a7.1780272022.git.m@maowtm.org>
On Mon, Jun 01, 2026 at 01:00:37AM +0100, Tingmao Wang wrote:
> The quietness behaviour is as documented in the previous patch.
>
> For optional accesses, since the existing deny_masks can only store 2x4bit
> of layer index, with no way to represent "no layer", we need to either
> expand it or have another field to correctly handle quieting of those.
> This commit uses the latter approach - we add another field to store which
> optional access (of the 2) are covered by quiet rules in their respective
> layers as stored in deny_masks.
>
> We can avoid making struct landlock_file_security larger by converting the
> existing fown_layer to a 4bit field. This commit does that, and adds test
> to ensure that it is large enough for LANDLOCK_MAX_NUM_LAYERS-1.
>
> Assisted-by: GitHub Copilot:claude-opus-4.7 copilot-review
> Signed-off-by: Tingmao Wang <m@maowtm.org>
> ---
>
> Changes in v10:
> - clang-format header file changes too
> - Fix grammar in some comments
>
> Changes in v9:
> - Fix conflict
> - Applied struct layer_masks changes to this as well.
> - Replace 4 with HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1) in
> landlock_get_quiet_optional_accesses()
> - Replace 4 with HWEIGHT in (existing) get_layer_from_deny_masks as
> well.
> - Use optional_access_t typedef for all quiet_optional_accesses values
> instead of u8
>
> Changes in v8:
> - Rebase on top of mic/next
> - Populate request.rule_flags in hook_unix_find()
>
> Changes in v7:
> - Following change in commit 1, now we need to copy rule_flags into
> landlock_request before calling landlock_log_denial for relevant fs
> denials
> - Remove left over param comment
>
> Changes in v5:
> - Update code style and comment in get_layer_from_deny_masks() and
> landlock_log_denial()
> - Now that rule_flags is moved into landlock_request, this version removes
> the extra parameter for landlock_log_denial and gets rid of
> no_rule_flags, simplifying some code.
> - Fix build failure without CONFIG_AUDIT (reported by Justin Suess)
>
> Changes in v3:
> - Renamed patch title from "Check for quiet flag in landlock_log_denial"
> to this given the growth.
> - Moved quiet bit check after domain_exec check
> - Rename, style and comment fixes suggested by Mickaël.
> - Squashed patch 6/6 from v2 "Implement quiet for optional accesses" into
> this one. Changes to that below:
> - Refactor the quiet flag setting in get_layer_from_deny_masks() to be
> more clear.
> - Add KUnit tests
> - Fix comments, add WARN_ON_ONCE, use __const_hweight64() as suggested by
> review
> - Move build_check_file_security to fs.c
> - Use a typedef for quiet_optional_accesses, add static_assert, and
> improve docs on landlock_get_quiet_optional_accesses.
>
> Changes in v2:
> - Supports the new quiet access masks.
> - Support quieting scope requests (but not ptrace and attempted mounting
> for now)
>
> security/landlock/access.h | 5 +
> security/landlock/audit.c | 261 ++++++++++++++++++++++++++++++++++---
> security/landlock/audit.h | 1 +
> security/landlock/domain.c | 33 +++++
> security/landlock/domain.h | 4 +
> security/landlock/fs.c | 29 +++++
> security/landlock/fs.h | 17 ++-
> security/landlock/net.c | 15 +--
> 8 files changed, 335 insertions(+), 30 deletions(-)
>
> diff --git a/security/landlock/access.h b/security/landlock/access.h
> index 42d8b5134358..8cc4ee3427e5 100644
> --- a/security/landlock/access.h
> +++ b/security/landlock/access.h
> @@ -141,4 +141,9 @@ static inline bool access_mask_subset(access_mask_t subset,
> return (subset | superset) == superset;
> }
>
> +/* A bitmask that is large enough to hold set of optional accesses. */
> +typedef u8 optional_access_t;
> +static_assert(BITS_PER_TYPE(optional_access_t) >=
> + HWEIGHT(_LANDLOCK_ACCESS_FS_OPTIONAL));
> +
> #endif /* _SECURITY_LANDLOCK_ACCESS_H */
> diff --git a/security/landlock/audit.c b/security/landlock/audit.c
> index 8c56f7f6467a..2d8197cc8fe3 100644
> --- a/security/landlock/audit.c
> +++ b/security/landlock/audit.c
> @@ -249,7 +249,9 @@ static void test_get_denied_layer(struct kunit *const test)
> static size_t
> get_layer_from_deny_masks(access_mask_t *const access_request,
> const access_mask_t all_existing_optional_access,
> - const deny_masks_t deny_masks)
> + const deny_masks_t deny_masks,
> + optional_access_t quiet_optional_accesses,
> + bool *quiet)
> {
> const unsigned long access_opt = all_existing_optional_access;
> const unsigned long access_req = *access_request;
> @@ -257,6 +259,7 @@ get_layer_from_deny_masks(access_mask_t *const access_request,
> size_t youngest_layer = 0;
> size_t access_index = 0;
> unsigned long access_bit;
> + bool should_quiet = false;
>
> /* This will require change with new object types. */
> WARN_ON_ONCE(access_opt != _LANDLOCK_ACCESS_FS_OPTIONAL);
> @@ -265,20 +268,33 @@ get_layer_from_deny_masks(access_mask_t *const access_request,
> BITS_PER_TYPE(access_mask_t)) {
> if (access_req & BIT(access_bit)) {
> const size_t layer =
> - (deny_masks >> (access_index * 4)) &
> + (deny_masks >>
> + (access_index *
> + HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1))) &
> (LANDLOCK_MAX_NUM_LAYERS - 1);
> + const bool layer_has_quiet =
> + !!(quiet_optional_accesses & BIT(access_index));
>
> if (layer > youngest_layer) {
> youngest_layer = layer;
> missing = BIT(access_bit);
> + should_quiet = layer_has_quiet;
> } else if (layer == youngest_layer) {
> missing |= BIT(access_bit);
> + /*
> + * Whether the layer has rules with quiet flag covering
> + * the file accessed does not depend on the access, and so
> + * the following WARN_ON_ONCE() should not fail.
> + */
> + WARN_ON_ONCE(should_quiet && !layer_has_quiet);
WARN_ON_ONCE(should_quiet != layer_has_quiet);
> + should_quiet = layer_has_quiet;
> }
> }
> access_index++;
> }
>
> *access_request = missing;
> + *quiet = should_quiet;
> return youngest_layer;
> }
>
> @@ -288,42 +304,188 @@ static void test_get_layer_from_deny_masks(struct kunit *const test)
> {
> deny_masks_t deny_mask;
> access_mask_t access;
> + optional_access_t quiet_optional_accesses;
> + bool quiet;
>
> /* truncate:0 ioctl_dev:2 */
> deny_mask = 0x20;
> + quiet_optional_accesses = 0;
>
> access = LANDLOCK_ACCESS_FS_TRUNCATE;
> KUNIT_EXPECT_EQ(test, 0,
> - get_layer_from_deny_masks(&access,
> - _LANDLOCK_ACCESS_FS_OPTIONAL,
> - deny_mask));
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
> + KUNIT_EXPECT_EQ(test, quiet, false);
> +
> + access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
> + KUNIT_EXPECT_EQ(test, 2,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
> + KUNIT_EXPECT_EQ(test, quiet, false);
> +
> + access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
> + KUNIT_EXPECT_EQ(test, 2,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
> + KUNIT_EXPECT_EQ(test, quiet, false);
> +
> + /* layer denying truncate: quiet, ioctl: not quiet */
> + quiet_optional_accesses = 0b01;
> +
> + access = LANDLOCK_ACCESS_FS_TRUNCATE;
> + KUNIT_EXPECT_EQ(test, 0,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
> + KUNIT_EXPECT_EQ(test, quiet, true);
> +
> + access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
> + KUNIT_EXPECT_EQ(test, 2,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
> + KUNIT_EXPECT_EQ(test, quiet, false);
> +
> + access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
> + KUNIT_EXPECT_EQ(test, 2,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
> + KUNIT_EXPECT_EQ(test, quiet, false);
> +
> + /* Reverse order - truncate:2 ioctl_dev:0 */
> + deny_mask = 0x02;
> + quiet_optional_accesses = 0;
> +
> + access = LANDLOCK_ACCESS_FS_TRUNCATE;
> + KUNIT_EXPECT_EQ(test, 2,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
> + KUNIT_EXPECT_EQ(test, quiet, false);
> +
> + access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
> + KUNIT_EXPECT_EQ(test, 0,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
> + KUNIT_EXPECT_EQ(test, quiet, false);
> +
> + access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
> + KUNIT_EXPECT_EQ(test, 2,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
> + KUNIT_EXPECT_EQ(test, quiet, false);
> +
> + /* layer denying truncate: quiet, ioctl: not quiet */
> + quiet_optional_accesses = 0b01;
> +
> + access = LANDLOCK_ACCESS_FS_TRUNCATE;
> + KUNIT_EXPECT_EQ(test, 2,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
> + KUNIT_EXPECT_EQ(test, quiet, true);
> +
> + access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
> + KUNIT_EXPECT_EQ(test, 0,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
> + KUNIT_EXPECT_EQ(test, quiet, false);
>
> access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
> KUNIT_EXPECT_EQ(test, 2,
> - get_layer_from_deny_masks(&access,
> - _LANDLOCK_ACCESS_FS_OPTIONAL,
> - deny_mask));
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
> + KUNIT_EXPECT_EQ(test, quiet, true);
> +
> + /* layer denying truncate: not quiet, ioctl: quiet */
> + quiet_optional_accesses = 0b10;
> +
> + access = LANDLOCK_ACCESS_FS_TRUNCATE;
> + KUNIT_EXPECT_EQ(test, 2,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
> + KUNIT_EXPECT_EQ(test, quiet, false);
> +
> + access = LANDLOCK_ACCESS_FS_IOCTL_DEV;
> + KUNIT_EXPECT_EQ(test, 0,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);
> + KUNIT_EXPECT_EQ(test, quiet, true);
> +
> + access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
> + KUNIT_EXPECT_EQ(test, 2,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
> + KUNIT_EXPECT_EQ(test, quiet, false);
>
> /* truncate:15 ioctl_dev:15 */
> deny_mask = 0xff;
> + quiet_optional_accesses = 0;
> +
> + access = LANDLOCK_ACCESS_FS_TRUNCATE;
> + KUNIT_EXPECT_EQ(test, 15,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
> + KUNIT_EXPECT_EQ(test, quiet, false);
> +
> + access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
> + KUNIT_EXPECT_EQ(test, 15,
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> + KUNIT_EXPECT_EQ(test, access,
> + LANDLOCK_ACCESS_FS_TRUNCATE |
> + LANDLOCK_ACCESS_FS_IOCTL_DEV);
> + KUNIT_EXPECT_EQ(test, quiet, false);
> +
> + /* Both quiet (same layer so quietness must be the same) */
> + quiet_optional_accesses = 0b11;
>
> access = LANDLOCK_ACCESS_FS_TRUNCATE;
> KUNIT_EXPECT_EQ(test, 15,
> - get_layer_from_deny_masks(&access,
> - _LANDLOCK_ACCESS_FS_OPTIONAL,
> - deny_mask));
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);
> + KUNIT_EXPECT_EQ(test, quiet, true);
>
> access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;
> KUNIT_EXPECT_EQ(test, 15,
> - get_layer_from_deny_masks(&access,
> - _LANDLOCK_ACCESS_FS_OPTIONAL,
> - deny_mask));
> + get_layer_from_deny_masks(
> + &access, _LANDLOCK_ACCESS_FS_OPTIONAL,
> + deny_mask, quiet_optional_accesses, &quiet));
> KUNIT_EXPECT_EQ(test, access,
> LANDLOCK_ACCESS_FS_TRUNCATE |
> LANDLOCK_ACCESS_FS_IOCTL_DEV);
> + KUNIT_EXPECT_EQ(test, quiet, true);
> }
>
> #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
> @@ -354,6 +516,22 @@ static bool is_valid_request(const struct landlock_request *const request)
> return true;
> }
>
> +static access_mask_t
> +pick_access_mask_for_request_type(const enum landlock_request_type type,
> + const struct access_masks access_masks)
> +{
> + switch (type) {
> + case LANDLOCK_REQUEST_FS_ACCESS:
> + return access_masks.fs;
> + case LANDLOCK_REQUEST_NET_ACCESS:
> + return access_masks.net;
> + default:
> + WARN_ONCE(1, "Invalid request type %d passed to %s", type,
> + __func__);
> + return 0;
> + }
> +}
> +
> /**
> * landlock_log_denial - Create audit records related to a denial
> *
> @@ -367,6 +545,7 @@ void landlock_log_denial(const struct landlock_cred_security *const subject,
> struct landlock_hierarchy *youngest_denied;
> size_t youngest_layer;
> access_mask_t missing;
> + bool object_quiet_flag = false, quiet_applicable_to_access = false;
>
> if (WARN_ON_ONCE(!subject || !subject->domain ||
> !subject->domain->hierarchy || !request))
> @@ -382,10 +561,15 @@ void landlock_log_denial(const struct landlock_cred_security *const subject,
> youngest_layer = get_denied_layer(subject->domain,
> &missing,
> request->layer_masks);
> + object_quiet_flag =
> + request->layer_masks->layers[youngest_layer]
> + .quiet;
> } else {
> youngest_layer = get_layer_from_deny_masks(
> &missing, _LANDLOCK_ACCESS_FS_OPTIONAL,
> - request->deny_masks);
> + request->deny_masks,
> + request->quiet_optional_accesses,
> + &object_quiet_flag);
> }
> youngest_denied =
> get_hierarchy(subject->domain, youngest_layer);
> @@ -420,6 +604,53 @@ void landlock_log_denial(const struct landlock_cred_security *const subject,
> return;
> }
>
> + /*
> + * Checks if the object is marked quiet by the layer that denied the
> + * request. If it's a different layer that marked it as quiet, but
> + * that layer is not the one that denied the request, we should still
> + * audit log the denial.
> + */
> + if (object_quiet_flag) {
> + /*
> + * We now check if the denied requests are all covered by the
> + * layer's quiet access bits.
> + */
> + const access_mask_t quiet_mask =
> + pick_access_mask_for_request_type(
> + request->type, youngest_denied->quiet_masks);
> +
> + quiet_applicable_to_access = (quiet_mask & missing) == missing;
> + } else {
> + /*
> + * Either the object is not quiet, or this is a scope request. We
> + * check request->type to distinguish between the two cases.
> + */
> + const access_mask_t quiet_mask =
> + youngest_denied->quiet_masks.scope;
> +
> + switch (request->type) {
> + case LANDLOCK_REQUEST_SCOPE_SIGNAL:
> + quiet_applicable_to_access =
> + !!(quiet_mask & LANDLOCK_SCOPE_SIGNAL);
> + break;
> + case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET:
> + quiet_applicable_to_access =
> + !!(quiet_mask &
> + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
> + break;
> + /*
> + * Leave LANDLOCK_REQUEST_PTRACE and
> + * LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY unhandled for now - they are
> + * never quiet.
> + */
> + default:
> + break;
> + }
> + }
> +
> + if (quiet_applicable_to_access)
> + return;
> +
> /* Uses consistent allocation flags wrt common_lsm_audit(). */
> ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN,
> AUDIT_LANDLOCK_ACCESS);
> diff --git a/security/landlock/audit.h b/security/landlock/audit.h
> index b85d752273ac..620f8a24291d 100644
> --- a/security/landlock/audit.h
> +++ b/security/landlock/audit.h
> @@ -48,6 +48,7 @@ struct landlock_request {
> /* Required fields for requests with deny masks. */
> const access_mask_t all_existing_optional_access;
> deny_masks_t deny_masks;
> + optional_access_t quiet_optional_accesses;
Please update is_valid_request() with the new invariant.
> };
>
> #ifdef CONFIG_AUDIT
> diff --git a/security/landlock/domain.c b/security/landlock/domain.c
> index d1a4d8b33ee1..60c356dacc83 100644
> --- a/security/landlock/domain.c
> +++ b/security/landlock/domain.c
> @@ -157,6 +157,39 @@ get_layer_deny_mask(const access_mask_t all_existing_optional_access,
> << ((access_weight - 1) * HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1));
> }
>
> +/**
> + * landlock_get_quiet_optional_accesses - Get optional accesses which are
> + * "covered" by quiet rule flags.
Please try to add a non-wrapping description (still 80 columns), or
align it (see other kdoc).
Check the kernel doc warnings:
@all_existing_optional_access:
@...
> + *
> + * Returns a bitmask of which optional accesses are denied by layers for
Return: a bitmask...
> + * which the quiet flag was collected during the path walk.
> + */
> +optional_access_t landlock_get_quiet_optional_accesses(
> + const access_mask_t all_existing_optional_access,
> + const deny_masks_t deny_masks, const struct layer_masks *const masks)
> +{
> + const unsigned long access_opt = all_existing_optional_access;
> + size_t access_index = 0;
> + unsigned long access_bit;
> + optional_access_t quiet_optional_accesses = 0;
> +
> + /* This will require change with new object types. */
> + WARN_ON_ONCE(access_opt != _LANDLOCK_ACCESS_FS_OPTIONAL);
> +
> + for_each_set_bit(access_bit, &access_opt,
> + BITS_PER_TYPE(access_mask_t)) {
> + const u8 layer =
> + (deny_masks >> (access_index *
> + HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1))) &
> + (LANDLOCK_MAX_NUM_LAYERS - 1);
> +
> + if (masks->layers[layer].quiet)
> + quiet_optional_accesses |= BIT(access_index);
> + access_index++;
> + }
> + return quiet_optional_accesses;
> +}
> +
> #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
>
> static void test_get_layer_deny_mask(struct kunit *const test)
> diff --git a/security/landlock/domain.h b/security/landlock/domain.h
> index 9f560f3c3bd1..2a1660e3dea7 100644
> --- a/security/landlock/domain.h
> +++ b/security/landlock/domain.h
> @@ -126,6 +126,10 @@ landlock_get_deny_masks(const access_mask_t all_existing_optional_access,
> const access_mask_t optional_access,
> const struct layer_masks *const masks);
>
> +optional_access_t landlock_get_quiet_optional_accesses(
> + const access_mask_t all_existing_optional_access,
> + const deny_masks_t deny_masks, const struct layer_masks *const masks);
> +
> int landlock_init_hierarchy_log(struct landlock_hierarchy *const hierarchy);
>
> static inline void
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index cc0852f77311..a8cb3179f815 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -1727,8 +1727,31 @@ get_required_file_open_access(const struct file *const file)
> return access;
> }
>
> +static void build_check_file_security(void)
> +{
> +#ifdef CONFIG_AUDIT
> + const struct landlock_file_security file_sec = {
> + .quiet_optional_accesses = ~0,
> + .fown_layer = ~0,
> + };
> +
> + /*
> + * Make sure quiet_optional_accesses has enough bits to cover all
> + * optional accesses. The use of __const_hweight64() rather than
> + * HWEIGHT() is due to GCC erroring about non-constants in
> + * BUILD_BUG_ON call when using the latter, and the use of the 64bit
> + * version is for future-proofing.
> + */
> + BUILD_BUG_ON(__const_hweight64((u64)file_sec.quiet_optional_accesses) <
> + __const_hweight64(_LANDLOCK_ACCESS_FS_OPTIONAL));
> + /* Makes sure all layers can be identified. */
> + BUILD_BUG_ON(file_sec.fown_layer < LANDLOCK_MAX_NUM_LAYERS - 1);
> +#endif /* CONFIG_AUDIT */
> +}
> +
> static int hook_file_alloc_security(struct file *const file)
> {
> + build_check_file_security();
> /*
> * Grants all access rights, even if most of them are not checked later
> * on. It is more consistent.
> @@ -1805,6 +1828,10 @@ static int hook_file_open(struct file *const file)
> #ifdef CONFIG_AUDIT
> landlock_file(file)->deny_masks = landlock_get_deny_masks(
> _LANDLOCK_ACCESS_FS_OPTIONAL, optional_access, &layer_masks);
> + landlock_file(file)->quiet_optional_accesses =
> + landlock_get_quiet_optional_accesses(
> + _LANDLOCK_ACCESS_FS_OPTIONAL,
> + landlock_file(file)->deny_masks, &layer_masks);
> #endif /* CONFIG_AUDIT */
>
> if (access_mask_subset(open_access_request, allowed_access))
> @@ -1841,6 +1868,7 @@ static int hook_file_truncate(struct file *const file)
> .access = LANDLOCK_ACCESS_FS_TRUNCATE,
> #ifdef CONFIG_AUDIT
> .deny_masks = landlock_file(file)->deny_masks,
> + .quiet_optional_accesses = landlock_file(file)->quiet_optional_accesses,
> #endif /* CONFIG_AUDIT */
> });
> return -EACCES;
> @@ -1880,6 +1908,7 @@ static int hook_file_ioctl_common(const struct file *const file,
> .access = LANDLOCK_ACCESS_FS_IOCTL_DEV,
> #ifdef CONFIG_AUDIT
> .deny_masks = landlock_file(file)->deny_masks,
> + .quiet_optional_accesses = landlock_file(file)->quiet_optional_accesses,
> #endif /* CONFIG_AUDIT */
> });
> return -EACCES;
> diff --git a/security/landlock/fs.h b/security/landlock/fs.h
> index cb7e654933ac..d0fca7da2466 100644
> --- a/security/landlock/fs.h
> +++ b/security/landlock/fs.h
> @@ -63,11 +63,20 @@ struct landlock_file_security {
> * _LANDLOCK_ACCESS_FS_OPTIONAL).
> */
> deny_masks_t deny_masks;
> + /**
> + * @quiet_optional_accesses: Stores which optional accesses are
> + * covered by quiet rules within the layer referred to in deny_masks,
> + * one access per bit. Does not take into account whether the quiet
> + * access bits are actually set in the layer's corresponding
> + * landlock_hierarchy.
> + */
> + optional_access_t quiet_optional_accesses
> + : HWEIGHT(_LANDLOCK_ACCESS_FS_OPTIONAL);
> /**
> * @fown_layer: Layer level of @fown_subject->domain with
> * LANDLOCK_SCOPE_SIGNAL.
> */
> - u8 fown_layer;
> + u8 fown_layer : 4;
Please don't hardcode such size.
Anyway, fown_layer can be updated concurrently (holding a lock), so we
should not convert it to a bitfield.
> #endif /* CONFIG_AUDIT */
>
> /**
> @@ -82,12 +91,6 @@ struct landlock_file_security {
>
> #ifdef CONFIG_AUDIT
>
> -/* Makes sure all layers can be identified. */
> -/* clang-format off */
> -static_assert((typeof_member(struct landlock_file_security, fown_layer))~0 >=
> - LANDLOCK_MAX_NUM_LAYERS);
> -/* clang-format off */
> -
> #endif /* CONFIG_AUDIT */
Remaining useless ifdef/endif.
>
> /**
> diff --git a/security/landlock/net.c b/security/landlock/net.c
> index 71868289748a..60894cff973e 100644
> --- a/security/landlock/net.c
> +++ b/security/landlock/net.c
> @@ -241,14 +241,13 @@ static int current_check_access_socket(struct socket *const sock,
>
> audit_net.family = address->sa_family;
> audit_net.sk = sock->sk;
> - landlock_log_denial(subject,
> - &(struct landlock_request){
> - .type = LANDLOCK_REQUEST_NET_ACCESS,
> - .audit.type = LSM_AUDIT_DATA_NET,
> - .audit.u.net = &audit_net,
> - .access = access_request,
> - .layer_masks = &layer_masks,
> - });
> + landlock_log_denial(
> + subject,
> + &(struct landlock_request){ .type = LANDLOCK_REQUEST_NET_ACCESS,
> + .audit.type = LSM_AUDIT_DATA_NET,
> + .audit.u.net = &audit_net,
> + .access = access_request,
> + .layer_masks = &layer_masks });
> return -EACCES;
> }
>
> --
> 2.54.0
^ permalink raw reply
* Re: [PATCH v10 4/9] samples/landlock: Add quiet flag support to sandboxer
From: Mickaël Salaün @ 2026-06-08 22:41 UTC (permalink / raw)
To: Tingmao Wang
Cc: Günther Noack, Justin Suess, Jan Kara, Abhinav Saxena,
linux-security-module
In-Reply-To: <9a90a123beb481c5be345e0d169eb14295803678.1780272022.git.m@maowtm.org>
As for LL_FORCE_LOG, using a QUIET flag not supported should exit with
an error.
On Mon, Jun 01, 2026 at 01:00:38AM +0100, Tingmao Wang wrote:
> Adds ability to set which access bits to quiet via LL_*_QUIET_ACCESS (FS,
> NET or SCOPED), and attach quiet flags to individual objects via
> LL_*_QUIET for FS and NET.
>
> Signed-off-by: Tingmao Wang <m@maowtm.org>
> ---
>
> Changes in v10:
> - Remove stray __attribute__((fallthrough)); (Thanks Justin for
> spotting)
>
> Changes in v9:
> - Add udp connect / bind quiet flag support
>
> Changes in v8:
> - Rebase on top of mic/next
> - populate_ruleset_net() already does not require the env var to be
> present, so remove redundant comment and check above
> populate_ruleset_net(ENV_NET_QUIET_NAME, ...).
>
> Changes in v6:
> - Make populate_ruleset_{fs,net} take a flags argument instead of a bool
> quiet (suggested by Justin Suess)
> - Fix if braces style
>
> Changes in v3:
> - Minor change to the above commit message.
>
> Changes in v2:
> - Added new environment variables to control which quiet access bits to
> set on the rule, and populate quiet_access_* from it.
> - Added support for quieting net rules and scoped access. Renamed patch
> title.
> - Increment ABI version
>
> samples/landlock/sandboxer.c | 133 ++++++++++++++++++++++++++++++++---
> 1 file changed, 122 insertions(+), 11 deletions(-)
>
> diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
> index 94e399e6b146..73a81ecd3696 100644
> --- a/samples/landlock/sandboxer.c
> +++ b/samples/landlock/sandboxer.c
> @@ -58,9 +58,14 @@ static inline int landlock_restrict_self(const int ruleset_fd,
>
> #define ENV_FS_RO_NAME "LL_FS_RO"
> #define ENV_FS_RW_NAME "LL_FS_RW"
> +#define ENV_FS_QUIET_NAME "LL_FS_QUIET"
> +#define ENV_FS_QUIET_ACCESS_NAME "LL_FS_QUIET_ACCESS"
> #define ENV_TCP_BIND_NAME "LL_TCP_BIND"
> #define ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT"
> +#define ENV_NET_QUIET_NAME "LL_NET_QUIET"
> +#define ENV_NET_QUIET_ACCESS_NAME "LL_NET_QUIET_ACCESS"
> #define ENV_SCOPED_NAME "LL_SCOPED"
> +#define ENV_SCOPED_QUIET_ACCESS_NAME "LL_SCOPED_QUIET_ACCESS"
> #define ENV_FORCE_LOG_NAME "LL_FORCE_LOG"
> #define ENV_UDP_BIND_NAME "LL_UDP_BIND"
> #define ENV_UDP_CONNECT_SEND_NAME "LL_UDP_CONNECT_SEND"
> @@ -119,7 +124,7 @@ static int parse_path(char *env_path, const char ***const path_list)
> /* clang-format on */
>
> static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
> - const __u64 allowed_access)
> + const __u64 allowed_access, __u32 flags)
> {
> int num_paths, i, ret = 1;
> char *env_path_name;
> @@ -169,7 +174,7 @@ static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
> if (!S_ISDIR(statbuf.st_mode))
> path_beneath.allowed_access &= ACCESS_FILE;
> if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
> - &path_beneath, 0)) {
> + &path_beneath, flags)) {
> fprintf(stderr,
> "Failed to update the ruleset with \"%s\": %s\n",
> path_list[i], strerror(errno));
> @@ -187,7 +192,7 @@ static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
> }
>
> static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
> - const __u64 allowed_access)
> + const __u64 allowed_access, __u32 flags)
> {
> int ret = 1;
> char *env_port_name, *env_port_name_next, *strport;
> @@ -215,7 +220,7 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
> }
> net_port.port = port;
> if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
> - &net_port, 0)) {
> + &net_port, flags)) {
> fprintf(stderr,
> "Failed to update the ruleset with port \"%llu\": %s\n",
> net_port.port, strerror(errno));
> @@ -303,6 +308,58 @@ static bool check_ruleset_scope(const char *const env_var,
>
> /* clang-format on */
>
> +static int add_quiet_access(__u64 *const quiet_access,
> + const __u64 handled_access,
> + const char *const env_var, const bool default_all)
> +{
> + char *env_quiet_access, *env_quiet_access_next, *str_access;
> +
> + if (default_all)
> + *quiet_access = handled_access;
> + else
> + *quiet_access = 0;
> +
> + env_quiet_access = getenv(env_var);
> + if (!env_quiet_access)
> + return 0;
> +
> + env_quiet_access = strdup(env_quiet_access);
> + env_quiet_access_next = env_quiet_access;
> + unsetenv(env_var);
> + *quiet_access = 0;
> +
> + while ((str_access = strsep(&env_quiet_access_next, ENV_DELIMITER))) {
> + if (strcmp(str_access, "") == 0)
> + continue;
> + else if (strcmp(str_access, "r") == 0)
> + *quiet_access |= ACCESS_FS_ROUGHLY_READ;
> + else if (strcmp(str_access, "w") == 0)
> + *quiet_access |= ACCESS_FS_ROUGHLY_WRITE;
> + else if (strcmp(str_access, "b") == 0)
> + *quiet_access |= LANDLOCK_ACCESS_NET_BIND_TCP;
What happen if we set "b" in LL_FS_QUIET_ACCESS?
> + else if (strcmp(str_access, "c") == 0)
> + *quiet_access |= LANDLOCK_ACCESS_NET_CONNECT_TCP;
> + else if (strcmp(str_access, "ub") == 0)
I don't really like these access-right names, they are not consistent.
All these env variables add a lot of complexity too. What about just
being able to quiet a path or a port? That would mean renaming
LL_FS_QUIET_ACCESS to LL_FS_QUIET.
Anyway, all should be unsetenv() unconditionally.
> + *quiet_access |= LANDLOCK_ACCESS_NET_BIND_UDP;
> + else if (strcmp(str_access, "uc") == 0)
> + *quiet_access |= LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
> + else if (strcmp(str_access, "a") == 0)
> + *quiet_access |= LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET;
> + else if (strcmp(str_access, "s") == 0)
> + *quiet_access |= LANDLOCK_SCOPE_SIGNAL;
> + else {
> + fprintf(stderr, "Unknown quiet access \"%s\"\n",
> + str_access);
> + free(env_quiet_access);
> + return -1;
> + }
> + }
> +
> + free(env_quiet_access);
> + *quiet_access &= handled_access;
> + return 0;
> +}
> +
> #define LANDLOCK_ABI_LAST 10
>
> #define XSTR(s) #s
> @@ -336,6 +393,22 @@ static const char help[] =
> "\n"
> "A sandboxer should not log denied access requests to avoid spamming logs, "
> "but to test audit we can set " ENV_FORCE_LOG_NAME "=1\n"
> + ENV_FS_QUIET_NAME " and " ENV_NET_QUIET_NAME ", both optional, can then be used "
> + "to make access to some denied paths or network ports not trigger audit logging.\n"
> + ENV_FS_QUIET_ACCESS_NAME " and " ENV_NET_QUIET_ACCESS_NAME " can be used to specify "
> + "which accesses should be quieted (defaults to all):\n"
> + "* " ENV_FS_QUIET_ACCESS_NAME ": file system accesses to quiet\n"
> + " - \"r\" to quiet all file/dir read accesses\n"
> + " - \"w\" to quiet all file/dir write accesses\n"
> + "* " ENV_NET_QUIET_ACCESS_NAME ": network accesses to quiet\n"
> + " - \"b\" to quiet tcp bind denials\n"
> + " - \"c\" to quiet tcp connect denials\n"
> + " - \"ub\" to quiet udp bind denials\n"
> + " - \"uc\" to quiet udp connect / send denials\n"
> + "In addition, " ENV_SCOPED_QUIET_ACCESS_NAME " can be set to quiet all denials for "
> + "scoped actions (defaults to none).\n"
> + " - \"a\" to quiet abstract unix socket denials\n"
> + " - \"s\" to quiet signal denials\n"
> "\n"
> "Example:\n"
> ENV_FS_RO_NAME "=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" "
> @@ -368,7 +441,12 @@ int main(const int argc, char *const argv[], char *const *const envp)
> LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
> .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
> LANDLOCK_SCOPE_SIGNAL,
> + .quiet_access_fs = 0,
> + .quiet_access_net = 0,
> + .quiet_scoped = 0,
> };
> +
Why this new line?
> + bool quiet_supported = true;
> int supported_restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON;
> int set_restrict_flags = 0;
>
> @@ -459,6 +537,8 @@ int main(const int argc, char *const argv[], char *const *const envp)
> ruleset_attr.handled_access_net &=
> ~(LANDLOCK_ACCESS_NET_BIND_UDP |
> LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP);
> + /* Don't add quiet flags for ABI < 10 later on. */
/* Removes quiet flags...
> + quiet_supported = false;
>
> /* Must be printed for any ABI < LANDLOCK_ABI_LAST. */
> fprintf(stderr,
> @@ -525,6 +605,25 @@ int main(const int argc, char *const argv[], char *const *const envp)
> unsetenv(ENV_FORCE_LOG_NAME);
> }
>
> + /*
> + * Add quiet for fs/net handled access bits. Doing this alone has no
> + * effect unless we later add quiet rules per FS_QUIET/NET_QUIET.
> + */
> + if (quiet_supported) {
> + if (add_quiet_access(&ruleset_attr.quiet_access_fs,
> + ruleset_attr.handled_access_fs,
> + ENV_FS_QUIET_ACCESS_NAME, true))
> + return 1;
> + if (add_quiet_access(&ruleset_attr.quiet_access_net,
> + ruleset_attr.handled_access_net,
> + ENV_NET_QUIET_ACCESS_NAME, true))
> + return 1;
> + if (add_quiet_access(&ruleset_attr.quiet_scoped,
> + ruleset_attr.scoped,
> + ENV_SCOPED_QUIET_ACCESS_NAME, false))
> + return 1;
> + }
> +
> ruleset_fd =
> landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> if (ruleset_fd < 0) {
> @@ -532,30 +631,42 @@ int main(const int argc, char *const argv[], char *const *const envp)
> return 1;
> }
>
> - if (populate_ruleset_fs(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro)) {
> + if (populate_ruleset_fs(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro, 0))
> goto err_close_ruleset;
> - }
> - if (populate_ruleset_fs(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) {
> + if (populate_ruleset_fs(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw, 0))
> goto err_close_ruleset;
> +
> + /* Don't require this env to be present. */
> + if (quiet_supported && getenv(ENV_FS_QUIET_NAME)) {
> + if (populate_ruleset_fs(ENV_FS_QUIET_NAME, ruleset_fd, 0,
> + LANDLOCK_ADD_RULE_QUIET))
> + goto err_close_ruleset;
> }
>
> if (populate_ruleset_net(ENV_TCP_BIND_NAME, ruleset_fd,
> - LANDLOCK_ACCESS_NET_BIND_TCP)) {
> + LANDLOCK_ACCESS_NET_BIND_TCP, 0)) {
> goto err_close_ruleset;
> }
> if (populate_ruleset_net(ENV_TCP_CONNECT_NAME, ruleset_fd,
> - LANDLOCK_ACCESS_NET_CONNECT_TCP)) {
> + LANDLOCK_ACCESS_NET_CONNECT_TCP, 0)) {
> goto err_close_ruleset;
> }
> if (populate_ruleset_net(ENV_UDP_BIND_NAME, ruleset_fd,
> - LANDLOCK_ACCESS_NET_BIND_UDP)) {
> + LANDLOCK_ACCESS_NET_BIND_UDP, 0)) {
> goto err_close_ruleset;
> }
> if (populate_ruleset_net(ENV_UDP_CONNECT_SEND_NAME, ruleset_fd,
> - LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP)) {
> + LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, 0)) {
> goto err_close_ruleset;
> }
>
> + if (quiet_supported) {
> + if (populate_ruleset_net(ENV_NET_QUIET_NAME, ruleset_fd, 0,
> + LANDLOCK_ADD_RULE_QUIET)) {
> + goto err_close_ruleset;
> + }
> + }
> +
> if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
> perror("Failed to restrict privileges");
> goto err_close_ruleset;
> --
> 2.54.0
^ permalink raw reply
* Re: [PATCH v10 9/9] selftests/landlock: Add tests for invalid use of quiet flag
From: Mickaël Salaün @ 2026-06-08 22:41 UTC (permalink / raw)
To: Tingmao Wang
Cc: Günther Noack, Justin Suess, Jan Kara, Abhinav Saxena,
linux-security-module
In-Reply-To: <99587e6f737a425d18fda31649edf304f74f3567.1780272022.git.m@maowtm.org>
On Mon, Jun 01, 2026 at 01:00:43AM +0100, Tingmao Wang wrote:
> Make sure that these calls return EINVAL.
>
> Signed-off-by: Tingmao Wang <m@maowtm.org>
> ---
>
> Changes in v4:
> - New patch
>
> tools/testing/selftests/landlock/base_test.c | 57 ++++++++++++++++++++
> 1 file changed, 57 insertions(+)
>
> diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
> index 84e91fcaa1b2..af9ad822a444 100644
> --- a/tools/testing/selftests/landlock/base_test.c
> +++ b/tools/testing/selftests/landlock/base_test.c
> @@ -526,4 +526,61 @@ TEST(cred_transfer)
> EXPECT_EQ(EACCES, errno);
> }
>
> +TEST(useless_quiet_rule)
> +{
> + struct landlock_ruleset_attr ruleset_attr = {
> + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
> + .quiet_access_fs = 0,
The other .quiet_* fields should also be tested.
> + };
> + struct landlock_path_beneath_attr path_beneath_attr = {
> + .allowed_access = LANDLOCK_ACCESS_FS_READ_DIR,
> + };
> + int ruleset_fd, root_fd;
> +
> + drop_caps(_metadata);
> + ruleset_fd =
> + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> + ASSERT_LE(0, ruleset_fd);
> +
> + root_fd = open("/", O_PATH | O_CLOEXEC);
> + ASSERT_LE(0, root_fd);
> + path_beneath_attr.parent_fd = root_fd;
> + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
> + &path_beneath_attr,
> + LANDLOCK_ADD_RULE_QUIET));
> + ASSERT_EQ(EINVAL, errno);
> +
> + /* Check that the rule had not been added. */
> + ASSERT_EQ(0, close(root_fd));
> + enforce_ruleset(_metadata, ruleset_fd);
> + ASSERT_EQ(0, close(ruleset_fd));
> +
> + ASSERT_EQ(-1, open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
> + ASSERT_EQ(EACCES, errno);
> +}
> +
> +TEST(invalid_quiet_bits_1)
> +{
> + struct landlock_ruleset_attr ruleset_attr = {
> + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
> + .quiet_access_fs = LANDLOCK_ACCESS_FS_WRITE_FILE,
ditto
> + };
> +
> + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr,
> + sizeof(ruleset_attr), 0));
> + ASSERT_EQ(EINVAL, errno);
> +}
> +
> +TEST(invalid_quiet_bits_2)
> +{
> + struct landlock_ruleset_attr ruleset_attr = {
> + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
> + .quiet_access_fs = 1ULL << 63,
ditto
> + };
> +
> + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr,
> + sizeof(ruleset_attr), 0));
> + ASSERT_EQ(EINVAL, errno);
> +}
> +
> TEST_HARNESS_MAIN
> --
> 2.54.0
>
^ permalink raw reply
* Re: [PATCH v10 2/9] landlock: Add API support and docs for the quiet flags
From: Mickaël Salaün @ 2026-06-08 22:41 UTC (permalink / raw)
To: Tingmao Wang
Cc: Günther Noack, Justin Suess, Jan Kara, Abhinav Saxena,
linux-security-module
In-Reply-To: <3f622c8e06638383d3dbf00b7bc78c61bfd584dd.1780272022.git.m@maowtm.org>
On Mon, Jun 01, 2026 at 01:00:36AM +0100, Tingmao Wang wrote:
> Adds the UAPI for the quiet flags feature (but not the implementation
> yet).
>
> Even though currently LANDLOCK_ADD_RULE_QUIET only affects audit
> logging, in the future this can also be used as part of a supervisor
> mechanism, where it will also suppress denial notifications on a
> per-object basis. Thus the name is deliberately generic, as opposed to
> e.g. LANDLOCK_ADD_RULE_LOG_QUIET.
>
> According to pahole, even after adding the struct access_masks quiet_masks
> in struct landlock_hierarchy, the u32 log_* bitfield still only has a size
> of 2 bytes, so there's minimal wasted space.
>
> Signed-off-by: Tingmao Wang <m@maowtm.org>
> ---
>
> Changes in v9:
> - Move a mistakenly included hunk into patch 1
> - Doc change for sys_landlock_create_ruleset to add missing
> "quiet_scoped | scoped == scoped" requirement.
> - Doc changes for struct landlock_ruleset_attr, and re-wrap added bits
> wider to stay consistent with the existing block.
> - Other style changes from suggestions
> - Added mention of this flag in the audit section of
> Documentation/admin-guide/LSM/landlock.rst
> - Added a block for this new flag to the "Previous limitations" section
> in Documentation/userspace-api/landlock.rst
>
> Changes in v8:
> - The new Landlock ABI version is now v10 as a result of rebase.
> - Allocate a rule_flags in hook_unix_find() and pass to
> is_access_to_paths_allowed().
>
> Changes in v6:
> - Fix typo in doc
>
> Changes in v5:
> - Doc fixes.
> - Fix build failure without CONFIG_AUDIT / CONFIG_INET (reported by Justin
> Suess)
>
> Changes in v4:
> - Minor update to this commit message.
> - Fix minor formatting
>
> Changes in v3:
> - Updated docs from Mickaël's suggestions.
>
> Changes in v2:
> - Per suggestion, added support for quieting only certain access bits,
> controlled by extra quiet_access_* fields in the ruleset_attr.
> - Added docs for the extra fields and made updates to doc changes in v1.
> In particular, call out that the effect of LANDLOCK_ADD_RULE_QUIET is
> independent from the access bits passed in rule_attr
> - landlock_add_rule will return -EINVAL when LANDLOCK_ADD_RULE_QUIET is
> used but the ruleset does not have any quiet access bits set for the
> given rule type.
> - ABI version bump to v8
> - Syntactic and comment changes per suggestion.
>
> Documentation/admin-guide/LSM/landlock.rst | 9 ++-
> Documentation/userspace-api/landlock.rst | 11 +++
> include/uapi/linux/landlock.h | 61 +++++++++++++++++
> security/landlock/domain.h | 5 ++
> security/landlock/fs.c | 4 +-
> security/landlock/fs.h | 2 +-
> security/landlock/net.c | 5 +-
> security/landlock/net.h | 5 +-
> security/landlock/ruleset.c | 12 +++-
> security/landlock/ruleset.h | 12 +++-
> security/landlock/syscalls.c | 71 +++++++++++++++-----
> tools/testing/selftests/landlock/base_test.c | 2 +-
> 12 files changed, 168 insertions(+), 31 deletions(-)
>
> diff --git a/Documentation/admin-guide/LSM/landlock.rst b/Documentation/admin-guide/LSM/landlock.rst
> index 9923874e2156..ccc32dad1d1c 100644
> --- a/Documentation/admin-guide/LSM/landlock.rst
> +++ b/Documentation/admin-guide/LSM/landlock.rst
> @@ -19,8 +19,10 @@ Audit
> Denied access requests are logged by default for a sandboxed program if `audit`
> is enabled. This default behavior can be changed with the
> sys_landlock_restrict_self() flags (cf.
> -Documentation/userspace-api/landlock.rst). Landlock logs can also be masked
> -thanks to audit rules. Landlock can generate 2 audit record types.
> +Documentation/userspace-api/landlock.rst), or suppressed on a per-object
> +basis by using ``LANDLOCK_ADD_RULE_QUIET`` (ABI 10+). Landlock logs can
> +also be masked thanks to audit rules. Landlock can generate 2 audit
> +record types.
>
> Record types
> ------------
> @@ -172,7 +174,8 @@ If you get spammed with audit logs related to Landlock, this is either an
> attack attempt or a bug in the security policy. We can put in place some
> filters to limit noise with two complementary ways:
>
> -- with sys_landlock_restrict_self()'s flags if we can fix the sandboxed
> +- with sys_landlock_restrict_self()'s flags, or
> + ``LANDLOCK_ADD_RULE_QUIET`` (ABI 10+) if we can fix the sandboxed
> programs,
> - or with audit rules (see :manpage:`auditctl(8)`).
>
> diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
> index 45861fa75685..138d504cb498 100644
> --- a/Documentation/userspace-api/landlock.rst
> +++ b/Documentation/userspace-api/landlock.rst
> @@ -722,6 +722,17 @@ Starting with the Landlock ABI version 9, it is possible to restrict
> connections to pathname UNIX domain sockets (:manpage:`unix(7)`) using
> the new ``LANDLOCK_ACCESS_FS_RESOLVE_UNIX`` right.
>
> +Quiet rule flag (ABI < 10)
> +-----------------------------------------
The line should be the same number of characters as the title.
> +
> +Starting with the Landlock ABI version 10, it is possible to selectively
> +suppress audit logs for specific denied accesses on a per-object basis with
> +the ``LANDLOCK_ADD_RULE_QUIET`` flag of sys_landlock_add_rule(), in
> +combination with the ``quiet_access_fs`` and ``quiet_access_net`` fields of
> +struct landlock_ruleset_attr. It is also now possible to suppress audit logs
> +for scope accesses via the ``quiet_scoped`` field of struct
> +landlock_ruleset_attr.
This doc should also quickly explain what happen when two rules with the
same object/inode are added with or without the flag.
> +
> .. _kernel_support:
>
> Kernel support
> diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
> index b147223efc97..90a0752b61bf 100644
> --- a/include/uapi/linux/landlock.h
> +++ b/include/uapi/linux/landlock.h
> @@ -32,6 +32,19 @@
> * *handle* a wide range or all access rights that they know about at build time
> * (and that they have tested with a kernel that supported them all).
> *
> + * @quiet_access_fs and @quiet_access_net are bitmasks of actions for which a
> + * denial by this layer will not trigger an audit log if the corresponding
> + * object (or its children, for filesystem rules) is marked with the "quiet" bit
> + * via %LANDLOCK_ADD_RULE_QUIET, even if logging would normally take place per
> + * landlock_restrict_self() flags. @quiet_scoped is similar, except that it
> + * does not require marking any objects as quiet - if the ruleset is created
> + * with any bits set in @quiet_scoped, then denial of such scoped resources will
> + * not trigger any log. These 3 fields are available since Landlock ABI version
> + * 10.
> + *
> + * @quiet_access_fs, @quiet_access_net and @quiet_scoped must be a subset of
> + * @handled_access_fs, @handled_access_net and @scoped respectively.
> + *
> * This structure can grow in future Landlock versions.
> */
> struct landlock_ruleset_attr {
> @@ -51,6 +64,21 @@ struct landlock_ruleset_attr {
> * resources (e.g. IPCs).
> */
> __u64 scoped;
> + /**
> + * @quiet_access_fs: Bitmask of filesystem actions which should not be
> + * logged if per-object quiet flag is set.
> + */
> + __u64 quiet_access_fs;
> + /**
> + * @quiet_access_net: Bitmask of network actions which should not be
> + * logged if per-object quiet flag is set.
> + */
> + __u64 quiet_access_net;
> + /**
> + * @quiet_scoped: Bitmask of scoped actions which should not be
> + * logged.
> + */
> + __u64 quiet_scoped;
> };
>
> /**
> @@ -69,6 +97,39 @@ struct landlock_ruleset_attr {
> #define LANDLOCK_CREATE_RULESET_ERRATA (1U << 1)
> /* clang-format on */
>
> +/**
> + * DOC: landlock_add_rule_flags
> + *
> + * **Flags**
> + *
> + * %LANDLOCK_ADD_RULE_QUIET
> + * Together with the quiet_* fields in struct landlock_ruleset_attr,
> + * this flag controls whether Landlock will log audit messages when
> + * access to the objects covered by this rule is denied by this layer.
> + *
> + * If audit logging is enabled, when Landlock denies an access, it will
> + * suppress the audit log if all of the following are true:
Still a lot of "audit log"...
> + *
> + * - this layer is the innermost layer that denied the access;
> + * - all accesses denied by this layer are part of the quiet_* fields
> + * in the related struct landlock_ruleset_attr;
> + * - the object (or one of its parents, for filesystem rules) is
> + * marked as "quiet" via %LANDLOCK_ADD_RULE_QUIET.
> + *
> + * Because logging is only suppressed by a layer if the layer denies
> + * access, a sandboxed program cannot use this flag to "hide" access
> + * denials, without denying itself the access in the first place.
> + *
> + * The effect of this flag does not depend on the value of
> + * allowed_access in the passed in rule_attr. When this flag is
> + * present, the caller is also allowed to pass in an empty
> + * allowed_access.
> + */
> +
> +/* clang-format off */
> +#define LANDLOCK_ADD_RULE_QUIET (1U << 0)
> +/* clang-format on */
> +
> /**
> * DOC: landlock_restrict_self_flags
> *
> diff --git a/security/landlock/domain.h b/security/landlock/domain.h
> index af100a8cd939..9f560f3c3bd1 100644
> --- a/security/landlock/domain.h
> +++ b/security/landlock/domain.h
> @@ -111,6 +111,11 @@ struct landlock_hierarchy {
> * %LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON. Set to false by default.
> */
> log_new_exec : 1;
> + /**
> + * @quiet_masks: Bitmasks of access that should be quieted (i.e. not
> + * logged) if the related object is marked as quiet.
> + */
> + struct access_masks quiet_masks;
> #endif /* CONFIG_AUDIT */
> };
>
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index f7c1bc64de20..cc0852f77311 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -325,7 +325,7 @@ static struct landlock_object *get_inode_object(struct inode *const inode)
> */
> int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
> const struct path *const path,
> - access_mask_t access_rights)
> + access_mask_t access_rights, const int flags)
const u32 flags (see syscall argument)
> {
> int err;
> struct landlock_id id = {
> @@ -346,7 +346,7 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
> if (IS_ERR(id.key.object))
> return PTR_ERR(id.key.object);
> mutex_lock(&ruleset->lock);
> - err = landlock_insert_rule(ruleset, id, access_rights);
> + err = landlock_insert_rule(ruleset, id, access_rights, flags);
> mutex_unlock(&ruleset->lock);
> /*
> * No need to check for an error because landlock_insert_rule()
> diff --git a/security/landlock/fs.h b/security/landlock/fs.h
> index bf9948941f2f..cb7e654933ac 100644
> --- a/security/landlock/fs.h
> +++ b/security/landlock/fs.h
> @@ -126,6 +126,6 @@ __init void landlock_add_fs_hooks(void);
>
> int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
> const struct path *const path,
> - access_mask_t access_hierarchy);
> + access_mask_t access_hierarchy, const int flags);
const u32 flags
>
> #endif /* _SECURITY_LANDLOCK_FS_H */
> diff --git a/security/landlock/net.c b/security/landlock/net.c
> index 981a362c24db..71868289748a 100644
> --- a/security/landlock/net.c
> +++ b/security/landlock/net.c
> @@ -20,7 +20,8 @@
> #include "ruleset.h"
>
> int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
> - const u16 port, access_mask_t access_rights)
> + const u16 port, access_mask_t access_rights,
> + const int flags)
const u32 flags
> {
> int err;
> const struct landlock_id id = {
> @@ -35,7 +36,7 @@ int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
> ~landlock_get_net_access_mask(ruleset, 0);
>
> mutex_lock(&ruleset->lock);
> - err = landlock_insert_rule(ruleset, id, access_rights);
> + err = landlock_insert_rule(ruleset, id, access_rights, flags);
> mutex_unlock(&ruleset->lock);
>
> return err;
> diff --git a/security/landlock/net.h b/security/landlock/net.h
> index 09960c237a13..72c47f4d6803 100644
> --- a/security/landlock/net.h
> +++ b/security/landlock/net.h
> @@ -16,7 +16,8 @@
> __init void landlock_add_net_hooks(void);
>
> int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
> - const u16 port, access_mask_t access_rights);
> + const u16 port, access_mask_t access_rights,
> + const int flags);
const u32 flags
> #else /* IS_ENABLED(CONFIG_INET) */
> static inline void landlock_add_net_hooks(void)
> {
> @@ -24,7 +25,7 @@ static inline void landlock_add_net_hooks(void)
>
> static inline int
> landlock_append_net_rule(struct landlock_ruleset *const ruleset, const u16 port,
> - access_mask_t access_rights)
> + access_mask_t access_rights, const int flags)
const u32 flags
> {
> return -EAFNOSUPPORT;
> }
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index 91948e406e69..f01c3e14e55d 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -21,6 +21,7 @@
> #include <linux/slab.h>
> #include <linux/spinlock.h>
> #include <linux/workqueue.h>
> +#include <uapi/linux/landlock.h>
>
> #include "access.h"
> #include "domain.h"
> @@ -255,6 +256,7 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
> if (WARN_ON_ONCE(this->layers[0].level != 0))
> return -EINVAL;
> this->layers[0].access |= (*layers)[0].access;
> + this->layers[0].flags.quiet |= (*layers)[0].flags.quiet;
> return 0;
> }
>
> @@ -305,12 +307,15 @@ static void build_check_layer(void)
> /* @ruleset must be locked by the caller. */
> int landlock_insert_rule(struct landlock_ruleset *const ruleset,
> const struct landlock_id id,
> - const access_mask_t access)
> + const access_mask_t access, const int flags)
const u32 flags
> {
> struct landlock_layer layers[] = { {
> .access = access,
> /* When @level is zero, insert_rule() extends @ruleset. */
> .level = 0,
> + .flags = {
> + .quiet = !!(flags & LANDLOCK_ADD_RULE_QUIET),
> + },
> } };
>
> build_check_layer();
> @@ -351,6 +356,7 @@ static int merge_tree(struct landlock_ruleset *const dst,
> return -EINVAL;
>
> layers[0].access = walker_rule->layers[0].access;
> + layers[0].flags = walker_rule->layers[0].flags;
>
> err = insert_rule(dst, id, &layers, ARRAY_SIZE(layers));
> if (err)
> @@ -581,6 +587,10 @@ landlock_merge_ruleset(struct landlock_ruleset *const parent,
> if (err)
> return ERR_PTR(err);
>
> +#ifdef CONFIG_AUDIT
> + new_dom->hierarchy->quiet_masks = ruleset->quiet_masks;
> +#endif /* CONFIG_AUDIT */
> +
> return no_free_ptr(new_dom);
> }
>
> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index f80ca487d125..d54bdb925e96 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -156,8 +156,8 @@ struct landlock_ruleset {
> * @work_free: Enables to free a ruleset within a lockless
> * section. This is only used by
> * landlock_put_ruleset_deferred() when @usage reaches zero.
> - * The fields @lock, @usage, @num_rules, @num_layers and
> - * @access_masks are then unused.
> + * The fields @lock, @usage, @num_rules, @num_layers, @quiet_masks
> + * and @access_masks are then unused.
> */
> struct work_struct work_free;
> struct {
> @@ -183,6 +183,12 @@ struct landlock_ruleset {
> * non-merged ruleset (i.e. not a domain).
> */
> u32 num_layers;
> + /**
> + * @quiet_masks: Stores the quiet flags for an unmerged
> + * ruleset. For a merged domain, this is stored in each
> + * layer's struct landlock_hierarchy instead.
> + */
> + struct access_masks quiet_masks;
> /**
> * @access_masks: Contains the subset of filesystem and
> * network actions that are restricted by a ruleset.
> @@ -213,7 +219,7 @@ DEFINE_FREE(landlock_put_ruleset, struct landlock_ruleset *,
>
> int landlock_insert_rule(struct landlock_ruleset *const ruleset,
> const struct landlock_id id,
> - const access_mask_t access);
> + const access_mask_t access, const int flags);
const u32 flags
>
> struct landlock_ruleset *
> landlock_merge_ruleset(struct landlock_ruleset *const parent,
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index d45469d5d464..08b6045d6926 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -105,8 +105,11 @@ static void build_check_abi(void)
> ruleset_size = sizeof(ruleset_attr.handled_access_fs);
> ruleset_size += sizeof(ruleset_attr.handled_access_net);
> ruleset_size += sizeof(ruleset_attr.scoped);
> + ruleset_size += sizeof(ruleset_attr.quiet_access_fs);
> + ruleset_size += sizeof(ruleset_attr.quiet_access_net);
> + ruleset_size += sizeof(ruleset_attr.quiet_scoped);
> BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
> - BUILD_BUG_ON(sizeof(ruleset_attr) != 24);
> + BUILD_BUG_ON(sizeof(ruleset_attr) != 48);
>
> path_beneath_size = sizeof(path_beneath_attr.allowed_access);
> path_beneath_size += sizeof(path_beneath_attr.parent_fd);
> @@ -193,6 +196,9 @@ const int landlock_abi_version = 10;
> * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
> * - %EINVAL: unknown @flags, or unknown access, or unknown scope, or too small
> * @size;
> + * - %EINVAL: quiet_access_fs, quiet_access_net, or quiet_scoped is not a
> + * subset of the corresponding handled_access_fs, handled_access_net, or
> + * scoped;
> * - %E2BIG: @attr or @size inconsistencies;
> * - %EFAULT: @attr or @size inconsistencies;
> * - %ENOMSG: empty &landlock_ruleset_attr.handled_access_fs.
> @@ -249,6 +255,21 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
> if ((ruleset_attr.scoped | LANDLOCK_MASK_SCOPE) != LANDLOCK_MASK_SCOPE)
> return -EINVAL;
>
> + /*
> + * Check that quiet masks are subsets of the respective handled masks.
> + * Because of the checks above this is sufficient to also ensure that
> + * the quiet masks are valid access masks.
> + */
> + if ((ruleset_attr.quiet_access_fs | ruleset_attr.handled_access_fs) !=
> + ruleset_attr.handled_access_fs)
> + return -EINVAL;
> + if ((ruleset_attr.quiet_access_net | ruleset_attr.handled_access_net) !=
> + ruleset_attr.handled_access_net)
> + return -EINVAL;
> + if ((ruleset_attr.quiet_scoped | ruleset_attr.scoped) !=
> + ruleset_attr.scoped)
> + return -EINVAL;
> +
> /* Checks arguments and transforms to kernel struct. */
> ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs,
> ruleset_attr.handled_access_net,
> @@ -256,6 +277,10 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
> if (IS_ERR(ruleset))
> return PTR_ERR(ruleset);
>
> + ruleset->quiet_masks.fs = ruleset_attr.quiet_access_fs;
> + ruleset->quiet_masks.net = ruleset_attr.quiet_access_net;
> + ruleset->quiet_masks.scope = ruleset_attr.quiet_scoped;
> +
> /* Creates anonymous FD referring to the ruleset. */
> ruleset_fd = anon_inode_getfd("[landlock-ruleset]", &ruleset_fops,
> ruleset, O_RDWR | O_CLOEXEC);
> @@ -320,7 +345,7 @@ static int get_path_from_fd(const s32 fd, struct path *const path)
> }
>
> static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
> - const void __user *const rule_attr)
> + const void __user *const rule_attr, int flags)
const u32 flags
> {
> struct landlock_path_beneath_attr path_beneath_attr;
> struct path path;
> @@ -335,9 +360,10 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
>
> /*
> * Informs about useless rule: empty allowed_access (i.e. deny rules)
> - * are ignored in path walks.
> + * are ignored in path walks. However, the rule is not useless if it
> + * is there to hold a quiet flag.
> */
> - if (!path_beneath_attr.allowed_access)
> + if (!flags && !path_beneath_attr.allowed_access)
> return -ENOMSG;
>
> /* Checks that allowed_access matches the @ruleset constraints. */
> @@ -345,6 +371,10 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
> if ((path_beneath_attr.allowed_access | mask) != mask)
> return -EINVAL;
>
> + /* Checks for useless quiet flag. */
> + if (flags & LANDLOCK_ADD_RULE_QUIET && !ruleset->quiet_masks.fs)
> + return -EINVAL;
> +
> /* Gets and checks the new rule. */
> err = get_path_from_fd(path_beneath_attr.parent_fd, &path);
> if (err)
> @@ -352,13 +382,13 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
>
> /* Imports the new rule. */
> err = landlock_append_fs_rule(ruleset, &path,
> - path_beneath_attr.allowed_access);
> + path_beneath_attr.allowed_access, flags);
> path_put(&path);
> return err;
> }
>
> static int add_rule_net_port(struct landlock_ruleset *ruleset,
> - const void __user *const rule_attr)
> + const void __user *const rule_attr, int flags)
const u32 flags
> {
> struct landlock_net_port_attr net_port_attr;
> int res;
> @@ -371,9 +401,10 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
>
> /*
> * Informs about useless rule: empty allowed_access (i.e. deny rules)
> - * are ignored by network actions.
> + * are ignored by network actions. However, the rule is not useless
> + * if it is there to hold a quiet flag.
> */
> - if (!net_port_attr.allowed_access)
> + if (!flags && !net_port_attr.allowed_access)
> return -ENOMSG;
>
> /* Checks that allowed_access matches the @ruleset constraints. */
> @@ -381,13 +412,17 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
> if ((net_port_attr.allowed_access | mask) != mask)
> return -EINVAL;
>
> + /* Checks for useless quiet flag. */
> + if (flags & LANDLOCK_ADD_RULE_QUIET && !ruleset->quiet_masks.net)
> + return -EINVAL;
> +
> /* Denies inserting a rule with port greater than 65535. */
> if (net_port_attr.port > U16_MAX)
> return -EINVAL;
>
> /* Imports the new rule. */
> return landlock_append_net_rule(ruleset, net_port_attr.port,
> - net_port_attr.allowed_access);
> + net_port_attr.allowed_access, flags);
> }
>
> /**
> @@ -398,7 +433,7 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
> * @rule_type: Identify the structure type pointed to by @rule_attr:
> * %LANDLOCK_RULE_PATH_BENEATH or %LANDLOCK_RULE_NET_PORT.
> * @rule_attr: Pointer to a rule (matching the @rule_type).
> - * @flags: Must be 0.
> + * @flags: Must be 0 or %LANDLOCK_ADD_RULE_QUIET.
> *
> * This system call enables to define a new rule and add it to an existing
> * ruleset.
> @@ -408,20 +443,25 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
> * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
> * - %EAFNOSUPPORT: @rule_type is %LANDLOCK_RULE_NET_PORT but TCP/IP is not
> * supported by the running kernel;
> - * - %EINVAL: @flags is not 0;
> + * - %EINVAL: @flags is not valid;
> * - %EINVAL: The rule accesses are inconsistent (i.e.
> * &landlock_path_beneath_attr.allowed_access or
> * &landlock_net_port_attr.allowed_access is not a subset of the ruleset
> * handled accesses)
> * - %EINVAL: &landlock_net_port_attr.port is greater than 65535;
> + * - %EINVAL: LANDLOCK_ADD_RULE_QUIET is passed but the ruleset has no
> + * quiet access bits set for the corresponding rule type.
> * - %ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access is
> - * 0);
> + * 0) and no flags;
> * - %EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
> * member of @rule_attr is not a file descriptor as expected;
> * - %EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of
> * @rule_attr is not the expected file descriptor type;
> * - %EPERM: @ruleset_fd has no write access to the underlying ruleset;
> * - %EFAULT: @rule_attr was not a valid address.
> + *
> + * .. kernel-doc:: include/uapi/linux/landlock.h
> + * :identifiers: landlock_add_rule_flags
> */
> SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
> const enum landlock_rule_type, rule_type,
> @@ -432,8 +472,7 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
> if (!is_initialized())
> return -EOPNOTSUPP;
>
> - /* No flag for now. */
> - if (flags)
> + if (flags && flags != LANDLOCK_ADD_RULE_QUIET)
> return -EINVAL;
>
> /* Gets and checks the ruleset. */
> @@ -443,9 +482,9 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
>
> switch (rule_type) {
> case LANDLOCK_RULE_PATH_BENEATH:
> - return add_rule_path_beneath(ruleset, rule_attr);
> + return add_rule_path_beneath(ruleset, rule_attr, flags);
> case LANDLOCK_RULE_NET_PORT:
> - return add_rule_net_port(ruleset, rule_attr);
> + return add_rule_net_port(ruleset, rule_attr, flags);
> default:
> return -EINVAL;
> }
> diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
> index 6c8113c2ded1..84e91fcaa1b2 100644
> --- a/tools/testing/selftests/landlock/base_test.c
> +++ b/tools/testing/selftests/landlock/base_test.c
> @@ -201,7 +201,7 @@ TEST(add_rule_checks_ordering)
> ASSERT_LE(0, ruleset_fd);
>
> /* Checks invalid flags. */
> - ASSERT_EQ(-1, landlock_add_rule(-1, 0, NULL, 1));
> + ASSERT_EQ(-1, landlock_add_rule(-1, 0, NULL, 100));
> ASSERT_EQ(EINVAL, errno);
>
> /* Checks invalid ruleset FD. */
> --
> 2.54.0
>
^ permalink raw reply
* Re: [PATCH v6 2/4] security: ima: introduce IMA_INIT_LATE_SYNC option
From: Mimi Zohar @ 2026-06-08 17:40 UTC (permalink / raw)
To: Yeoreum Yun, linux-security-module, linux-kernel, linux-integrity
Cc: paul, roberto.sassu, noodles, jarkko, sudeep.holla, jmorris,
serge, dmitry.kasatkin, eric.snowberg, jgg
In-Reply-To: <20260605144325.434436-3-yeoreum.yun@arm.com>
On Fri, 2026-06-05 at 15:43 +0100, Yeoreum Yun wrote:
> To generate the boot_aggregate log in the IMA subsystem with
> TPM PCR values, the TPM driver must be built as built-in and
> must be probed before the IMA subsystem is initialized.
>
> However, when the TPM device operates over the FF-A protocol using
> the CRB interface, probing fails and returns -EPROBE_DEFER if
> the tpm_crb_ffa device — an FF-A device that provides the communication
> interface to the tpm_crb driver — has not yet been probed.
>
> To ensure the TPM device operating over the FF-A protocol with
> the CRB interface is probed before IMA initialization,
> the following conditions must be met:
>
> 1. The corresponding ffa_device must be registered,
> which is done via ffa_init().
>
> 2. The tpm_crb_driver must successfully probe this device via
> tpm_crb_ffa_init().
>
> 3. The tpm_crb driver using CRB over FF-A can then
> be probed successfully. (See crb_acpi_add() and
> tpm_crb_ffa_init() for reference.)
>
> Unfortunately, ffa_init(), tpm_crb_ffa_init(), and crb_acpi_driver_init()
> are all registered with device_initcall, which means
> crb_acpi_driver_init() may be invoked before ffa_init() and
> tpm_crb_ffa_init() are completed.
>
> When this occurs, probing the TPM device is deferred.
> However, the deferred probe can happen after the IMA subsystem
> has already been initialized, since IMA initialization is performed
> during late_initcall, and deferred_probe_initcall() is performed
> at the same level.
>
> And the similar situation is reported on TPM devices attached on SPI
> bus[0].
>
> To resolve this, introduce IMA_INIT_LATE_SYNC option to initialise
> IMA at late_inicall_sync so that IMA is initialized with the TPM
> device probed deferred.
>
> When this option is enabled, modules that access files in the
> initramfs through usermode helper calls such as request_module()
> during initcall must not be built-in. Otherwise, IMA may miss
> measuring those files [1].
>
> Link: https://lore.kernel.org/all/aYXEepLhUouN5f99@earth.li/ [0]
> Link: https://lore.kernel.org/all/2b3782398cc17ce9d355490a0c42ebce9120a9ae.camel@linux.ibm.com/ [1]
> Suggested-by: Mimi Zohar <zohar@linux.ibm.com>
> Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
> Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
> ---
> security/integrity/ima/Kconfig | 10 ++++++++++
> security/integrity/ima/ima_main.c | 4 ++++
> 2 files changed, 14 insertions(+)
>
> diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
> index 862fbee2b174..75f71401fba3 100644
> --- a/security/integrity/ima/Kconfig
> +++ b/security/integrity/ima/Kconfig
> @@ -332,4 +332,14 @@ config IMA_KEXEC_EXTRA_MEMORY_KB
> If set to the default value of 0, an extra half page of memory for those
> additional measurements will be allocated.
>
> +config IMA_INIT_LATE_SYNC
> + bool "Initialise IMA at late_initcall_sync"
> + default n
> + help
> + This option initialises IMA at late_initcall_sync for platforms
> + where TPM device probing is deferred.
> + When this option is enabled, modules that access files in the
> + initramfs through usermode helper calls such as request_module()
> + during initcall must not be built-in. Otherwise, IMA may miss
> + file measurements for them.
> endif
I fixed the merge conflict with the "ima: Exporting and deleting IMA measurement
records from kernel memory" patch set. These patches are now queued in next-
integrity-testing awaiting Paul's Ack.
Mimi
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 5cea53fc36df..1cfae4b83dc5 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -1337,5 +1337,9 @@ DEFINE_LSM(ima) = {
> .order = LSM_ORDER_LAST,
> .blobs = &ima_blob_sizes,
> /* Start IMA after the TPM is available */
> +#ifndef CONFIG_IMA_INIT_LATE_SYNC
> .initcall_late = init_ima,
> +#else
> + .initcall_late_sync = init_ima,
> +#endif
> };
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox