From: "Mickaël Salaün" <mic@digikod.net>
To: Tingmao Wang <m@maowtm.org>
Cc: "Günther Noack" <gnoack3000@gmail.com>,
"Justin Suess" <utilityemal77@gmail.com>,
"Jan Kara" <jack@suse.cz>, "Abhinav Saxena" <xandfury@gmail.com>,
linux-security-module@vger.kernel.org
Subject: Re: [PATCH v10 3/9] landlock: Suppress logging when quiet flag is present
Date: Tue, 9 Jun 2026 00:41:32 +0200 [thread overview]
Message-ID: <20260608.daeshu7Leequ@digikod.net> (raw)
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
next prev parent reply other threads:[~2026-06-08 22:51 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-01 0:00 [PATCH v10 0/9] Implement LANDLOCK_ADD_RULE_QUIET Tingmao Wang
2026-06-01 0:00 ` [PATCH v10 1/9] landlock: Add a place for flags to layer rules Tingmao Wang
2026-06-08 22:40 ` Mickaël Salaün
2026-06-01 0:00 ` [PATCH v10 2/9] landlock: Add API support and docs for the quiet flags Tingmao Wang
2026-06-08 22:41 ` Mickaël Salaün
2026-06-01 0:00 ` [PATCH v10 3/9] landlock: Suppress logging when quiet flag is present Tingmao Wang
2026-06-08 22:41 ` Mickaël Salaün [this message]
2026-06-01 0:00 ` [PATCH v10 4/9] samples/landlock: Add quiet flag support to sandboxer Tingmao Wang
2026-06-08 22:41 ` Mickaël Salaün
2026-06-01 0:00 ` [PATCH v10 5/9] selftests/landlock: Replace hard-coded 16 with a constant Tingmao Wang
2026-06-01 0:00 ` [PATCH v10 6/9] selftests/landlock: add tests for quiet flag with fs rules Tingmao Wang
2026-06-05 19:04 ` Justin Suess
2026-06-08 1:31 ` Tingmao Wang
2026-06-01 0:00 ` [PATCH v10 7/9] selftests/landlock: add tests for quiet flag with net rules Tingmao Wang
2026-06-01 0:00 ` [PATCH v10 8/9] selftests/landlock: Add tests for quiet flag with scope Tingmao Wang
2026-06-01 0:00 ` [PATCH v10 9/9] selftests/landlock: Add tests for invalid use of quiet flag Tingmao Wang
2026-06-08 22:41 ` Mickaël Salaün
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260608.daeshu7Leequ@digikod.net \
--to=mic@digikod.net \
--cc=gnoack3000@gmail.com \
--cc=jack@suse.cz \
--cc=linux-security-module@vger.kernel.org \
--cc=m@maowtm.org \
--cc=utilityemal77@gmail.com \
--cc=xandfury@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox