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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.