]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
landlock: Suppress logging when quiet flag is present
authorTingmao Wang <m@maowtm.org>
Fri, 12 Jun 2026 01:48:49 +0000 (02:48 +0100)
committerMickaël Salaün <mic@digikod.net>
Sun, 14 Jun 2026 18:17:19 +0000 (20:17 +0200)
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.

Assisted-by: GitHub-Copilot:claude-opus-4.8 copilot-review
Signed-off-by: Tingmao Wang <m@maowtm.org>
Link: https://patch.msgid.link/2510a357a94183683eefc49917dcb2240d67be96.1781228815.git.m@maowtm.org
[mic: Cosmetic fixes]
Signed-off-by: Mickaël Salaün <mic@digikod.net>
security/landlock/access.h
security/landlock/audit.c
security/landlock/audit.h
security/landlock/domain.c
security/landlock/domain.h
security/landlock/fs.c
security/landlock/fs.h

index ce374a65f865d6b8b766a3b8d14b16e8dd3767bf..d926078bf0a5f5cd976074410479c72078346da4 100644 (file)
@@ -143,4 +143,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 */
index 8c56f7f6467a4d47217910a9871a9eed68852681..50536c5685262efe65d535f9498fd752b1c2f57e 100644 (file)
@@ -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,34 @@ 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);
+                               should_quiet = layer_has_quiet;
                        }
                }
                access_index++;
        }
 
        *access_request = missing;
+       *quiet = should_quiet;
        return youngest_layer;
 }
 
@@ -288,42 +305,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));
+                       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, 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 */
@@ -349,11 +512,34 @@ static bool is_valid_request(const struct landlock_request *const request)
        if (request->deny_masks) {
                if (WARN_ON_ONCE(!request->all_existing_optional_access))
                        return false;
+               static_assert(sizeof(request->all_existing_optional_access) ==
+                             sizeof(u32));
+               if (WARN_ON_ONCE(
+                           request->quiet_optional_accesses >=
+                           BIT(hweight32(
+                                   request->all_existing_optional_access))))
+                       return false;
        }
 
        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 +553,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 +569,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 +612,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);
index b85d752273ac2cf672d9660383bd9696af43290f..620f8a24291d5df39201a5ed1ed08bf0cbe96cef 100644 (file)
@@ -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;
 };
 
 #ifdef CONFIG_AUDIT
index 4ae45b30007193c08f07d22ae7e26ccd6ed28c08..9a8355fccd26ebb73922e8d67210186ad1eb27fd 100644 (file)
@@ -157,6 +157,44 @@ 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.
+ *
+ * @all_existing_optional_access: Bitmask of valid optional accesses.
+ * @deny_masks: Domain layer levels that denied each optional access (the
+ *              deny_masks field on struct landlock_file_security).
+ * @masks: The struct layer_masks collected during the path walk.
+ *
+ * Return: a bitmask of which optional accesses are denied by layers for 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)
index 9f560f3c3bd1aeb47470fc50e41c857cdf40c7ae..2a1660e3dea7e06420e1592914758b3d15582759 100644 (file)
@@ -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
index bd68a752abbfbfd81f1496670606906e68d3849e..6292887e6cefd63cd7e190a5871cf0525aa7b2ce 100644 (file)
@@ -1805,6 +1805,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 +1845,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 +1885,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;
index e4c530511360896eddf12e5093f81c433ae982dc..b4421d9df68ff7ea4772ce7e3d19ae746c06b103 100644 (file)
@@ -63,6 +63,13 @@ 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;
        /**
         * @fown_layer: Layer level of @fown_subject->domain with
         * LANDLOCK_SCOPE_SIGNAL.
@@ -96,7 +103,15 @@ struct landlock_file_security {
 /* clang-format off */
 static_assert((typeof_member(struct landlock_file_security, fown_layer))~0 >=
              LANDLOCK_MAX_NUM_LAYERS);
-/* clang-format off */
+/* clang-format on */
+
+/*
+ * Make sure quiet_optional_accesses has enough bits to cover all optional
+ * accesses.
+ */
+static_assert(BITS_PER_TYPE(typeof_member(struct landlock_file_security,
+                                         quiet_optional_accesses)) >=
+             HWEIGHT(_LANDLOCK_ACCESS_FS_OPTIONAL));
 
 #endif /* CONFIG_AUDIT */