[PATCH v4 11/19] landlock: Add tracepoints for rule checking

Mickaël Salaün <[email protected]>
Newsgroups org.kernel.vger.linux-security-module,org.kernel.vger.linux-trace-kernel
Message-ID <[email protected]>
Merge landlock_find_rule() into landlock_unmask_layers() so rule
pointers stay inside the domain implementation while unmask checking
gets the matched rule it needs for the check_rule tracepoint.
landlock_unmask_layers() now takes a landlock_id and the domain instead
of a rule pointer.  A rename or link evaluates the same dentry against
both renamed parents, so this path now looks the rule up once per
parent; collapsing that back to a single lookup is left to a follow-up.

Emit, via the per-type wrappers unmask_layers_fs() and
unmask_layers_net(), the rights each matching rule grants at every
domain layer.  The events carry this as a dynamic per-layer array (up to
LANDLOCK_MAX_NUM_LAYERS entries) reserved from the trace ring buffer,
not the caller's stack, and rendered symbolically per layer.  A
WARN_ON_ONCE() in __trace_landlock_fill_layers() flags a rule whose
layer levels fall outside the domain range or are unsorted, a
cannot-happen case; the zero-filled slots keep the rendered output and
the array bounds safe regardless.

Setting allowed_parent2 to true for non-dom-check requests when
get_inode_id() returns false preserves the pre-refactoring behavior: a
negative dentry (no backing inode) has no matching rule, so the access
is allowed at this path component.  Before the refactoring,
landlock_unmask_layers() with a NULL rule produced this result as a side
effect; now the caller must set it explicitly.

Name the trace-only check_rule fields so each printk label equals its
ring-buffer field name and works directly as an ftrace filter: the
request field is labelled access_request= and the per-layer array is
named grants.  Values audit also logs keep audit's label (domain=,
ruleset=) so a single filter works across trace and audit.

Cc: Günther Noack <[email protected]>
Cc: Justin Suess <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Mathieu Desnoyers <[email protected]>
Cc: Steven Rostedt <[email protected]>
Cc: Tingmao Wang <[email protected]>
Signed-off-by: Mickaël Salaün <[email protected]>
---

Changes since v3:
https://patch.msgid.link/[email protected]
- Move the layer helpers (__trace_landlock_fill_layers,
  __trace_landlock_print_layers) into the single shared
  CREATE_TRACE_POINTS helper block instead of a separate second block.

Changes since v2:
https://patch.msgid.link/[email protected]
- Order the check_rule tracepoint arguments with the common part
  (domain, rule) before the event-specific access_request and object.
- Reformatted TP_STRUCT__entry and TP_fast_assign to kernel tracing
  convention (requested by Steven Rostedt).
- Render the request access right as symbolic names with
  __print_flags(), shared with the audit blocker names.
- Rename the per-layer field label allowed= to grants= and render it
  symbolically via __print_landlock_layers(), intersected with the
  request to show the granted requested rights per layer (was a raw hex
  value).
- Rename struct layer_access_masks to the base's struct layer_masks.
- Rename the check_rule printk label request= to access_request= and the
  per-layer ring-buffer field layers to grants, so each trace-only field
  and its printk label share one name (usable directly as an ftrace
  filter); audit-shared labels (domain=, ruleset=) are unchanged.

Changes since v1:
https://patch.msgid.link/[email protected]
- Merged find-rule consolidation (v1 2/5) into this patch.
- Added check_rule_net tracepoint for network rules.
- Added get_inode_id() helper with rcu_access_pointer().
- Added allowed_parent2 behavioral fix.
---
 include/trace/events/landlock.h | 196 ++++++++++++++++++++++++++++++++
 security/landlock/domain.c      |  32 ++++--
 security/landlock/domain.h      |  10 +-
 security/landlock/fs.c          | 149 ++++++++++++++++--------
 security/landlock/net.c         |  21 +++-
 5 files changed, 343 insertions(+), 65 deletions(-)

diff --git a/include/trace/events/landlock.h b/include/trace/events/landlock.h
index 43bf126eb449..166c9bb544c0 100644
--- a/include/trace/events/landlock.h
+++ b/include/trace/events/landlock.h
@@ -16,8 +16,10 @@
 #include <linux/tracepoint.h>
 #include <linux/trace_seq.h>
 
+struct dentry;
 struct landlock_domain;
 struct landlock_hierarchy;
+struct landlock_rule;
 struct landlock_ruleset;
 struct path;
 
@@ -63,6 +65,77 @@ __trace_print_untrusted_str(struct trace_seq *p, const char *src, size_t len)
 	return ret;
 }
 
+/*
+ * Fills the dense per-domain-layer array layers (one access mask per layer,
+ * indexed by level - 1) from rule's sparse layer stack, keeping only the
+ * requested rights (access_request).  Layers with no matching rule entry get
+ * a zero mask.  Shared by the check_rule_fs and check_rule_net events.
+ *
+ * rule->layers is sorted by ascending level, with levels in the domain's
+ * [1, num_layers] range (see landlock_merge_ruleset()), so every entry maps
+ * to a slot.  A leftover entry would be a malformed rule; the zero-filled
+ * slots keep the output and the array bounds safe regardless.
+ */
+static inline void
+__trace_landlock_fill_layers(access_mask_t *const layers,
+			     const size_t num_layers,
+			     const struct landlock_rule *const rule,
+			     const access_mask_t access_request)
+{
+	size_t i = 0;
+
+	for (size_t level = 1; level <= num_layers; level++) {
+		access_mask_t grants = 0;
+
+		if (i < rule->num_layers && level == rule->layers[i].level) {
+			grants = rule->layers[i].access & access_request;
+			i++;
+		}
+		layers[level - 1] = grants;
+	}
+
+	/* A leftover entry means an out-of-range or unsorted rule level. */
+	WARN_ON_ONCE(i < rule->num_layers);
+}
+
+/*
+ * Renders the dense per-domain-layer access array as symbolic flag names for
+ * the grants field: layers wrapped in "{}", flags within a layer joined by
+ * "|", layers separated by ",", an empty layer rendered as nothing.
+ * Open-codes the flag walk because trace_print_flags_seq() NUL-terminates per
+ * call and so cannot be chained into a single field.  The shared names table
+ * covers every access right, so masked bits are always named.  Returns the
+ * trace_seq position like __print_flags().
+ */
+static inline const char *__trace_landlock_print_layers(
+	struct trace_seq *p, const access_mask_t *const layers,
+	const size_t num_layers, const struct trace_print_flags *const names,
+	const size_t names_size)
+{
+	const char *const ret = trace_seq_buffer_ptr(p);
+
+	trace_seq_putc(p, '{');
+	for (size_t i = 0; i < num_layers; i++) {
+		access_mask_t mask = layers[i];
+		bool first = true;
+
+		if (i)
+			trace_seq_putc(p, ',');
+		for (size_t j = 0; mask && j < names_size; j++) {
+			if ((mask & names[j].mask) != names[j].mask)
+				continue;
+			if (!first)
+				trace_seq_putc(p, '|');
+			trace_seq_puts(p, names[j].name);
+			mask &= ~names[j].mask;
+			first = false;
+		}
+	}
+	trace_seq_putc(p, '}');
+	trace_seq_putc(p, 0);
+	return ret;
+}
+
 #endif /* CREATE_TRACE_POINTS */
 
 /* clang-format off */
@@ -123,7 +196,33 @@ __trace_print_untrusted_str(struct trace_seq *p, const char *src, size_t len)
  * (e.g. network ports are __u64 in host endianness, like
  * landlock_net_port_attr.port).  Per-event details, such as where a value
  * is byte-swapped, live in the field's own kdoc.
+ *
+ * Rule-check fields
+ * ~~~~~~~~~~~~~~~~~
+ *
+ * The check_rule events fire during an access check, once per matching
+ * rule, before the final allow-or-deny verdict.  They share domain (the
+ * enforcing domain being evaluated), access_request (the access mask being
+ * checked), and rule (the matching rule, with per-layer access masks).
+ */
+
+/*
+ * Prints a per-layer access mask array (the dynamic array @array) as symbolic
+ * flag names using the shared @flag_names list (a _LANDLOCK_*_NAMES macro).
+ * Stays outside CREATE_TRACE_POINTS: TP_printk is expanded in the print-output
+ * pass where that macro is undefined.
  */
+#define __print_landlock_layers(array, flag_names...)			\
+	({								\
+		static const struct trace_print_flags __layer_names[] = { \
+			flag_names					\
+		};							\
+		__trace_landlock_print_layers(				\
+			p, __get_dynamic_array(array),			\
+			__get_dynamic_array_len(array) /		\
+				sizeof(access_mask_t),			\
+			__layer_names, ARRAY_SIZE(__layer_names));	\
+	})
 
 /**
  * landlock_create_ruleset - New ruleset created
@@ -432,6 +531,103 @@ TRACE_EVENT(landlock_free_domain,
 		__entry->domain_id, __entry->denials)
 );
 
+/**
+ * landlock_check_rule_fs - Filesystem rule evaluated during access check
+ *
+ * @domain: Enforcing domain (never NULL).
+ * @rule: Matching rule with per-layer access masks (never NULL).
+ * @access_request: Access mask evaluated against the rule (the domain's
+ *                   handled mask during rename/link double-checks).
+ * @dentry: Filesystem dentry being checked (never NULL).
+ *
+ * Emitted for each rule that matches during a filesystem access check.
+ * The grants array shows the requested rights the rule grants at each
+ * domain layer.  See Documentation/trace/events-landlock.rst for how to
+ * interpret it.
+ */
+TRACE_EVENT(landlock_check_rule_fs,
+
+	TP_PROTO(const struct landlock_domain *domain,
+		 const struct landlock_rule *rule,
+		 access_mask_t access_request, const struct dentry *dentry),
+
+	TP_ARGS(domain, rule, access_request, dentry),
+
+	TP_STRUCT__entry(
+		__field(	__u64,		domain_id	)
+		__field(	access_mask_t,	access_request	)
+		__field(	dev_t,		dev		)
+		__field(	ino_t,		ino		)
+		__dynamic_array(access_mask_t,	grants,
+				domain->num_layers)
+	),
+
+	TP_fast_assign(
+		__entry->domain_id	= domain->hierarchy->id;
+		__entry->access_request	= access_request;
+		__entry->dev		= dentry->d_sb->s_dev;
+		__entry->ino		= d_backing_inode(dentry)->i_ino;
+
+		__trace_landlock_fill_layers(__get_dynamic_array(grants),
+					     __get_dynamic_array_len(grants) /
+						     sizeof(access_mask_t),
+					     rule, access_request);
+	),
+
+	TP_printk("domain=%llx access_request=%s dev=%u:%u ino=%lu grants=%s",
+		__entry->domain_id,
+		__print_flags(__entry->access_request, "|", _LANDLOCK_ACCESS_FS_NAMES),
+		MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino,
+		__print_landlock_layers(grants, _LANDLOCK_ACCESS_FS_NAMES))
+);
+
+/**
+ * landlock_check_rule_net - Network port rule evaluated during access check
+ *
+ * @domain: Enforcing domain (never NULL).
+ * @rule: Matching rule with per-layer access masks (never NULL).
+ * @access_request: Access mask being requested.
+ * @port: Network port being checked (host endianness).
+ *
+ * Emitted for each rule that matches during a network access check.  The
+ * grants array shows the requested rights the rule grants at each domain
+ * layer.  See Documentation/trace/events-landlock.rst for how to
+ * interpret it.
+ */
+TRACE_EVENT(landlock_check_rule_net,
+
+	TP_PROTO(const struct landlock_domain *domain,
+		 const struct landlock_rule *rule,
+		 access_mask_t access_request, __u64 port),
+
+	TP_ARGS(domain, rule, access_request, port),
+
+	TP_STRUCT__entry(
+		__field(	__u64,		domain_id	)
+		__field(	access_mask_t,	access_request	)
+		__field(	__u64,		port		)
+		__dynamic_array(access_mask_t,	grants,
+				domain->num_layers)
+	),
+
+	TP_fast_assign(
+		__entry->domain_id	= domain->hierarchy->id;
+		__entry->access_request	= access_request;
+		__entry->port		= port;
+
+		__trace_landlock_fill_layers(__get_dynamic_array(grants),
+					     __get_dynamic_array_len(grants) /
+						     sizeof(access_mask_t),
+					     rule, access_request);
+	),
+
+	TP_printk("domain=%llx access_request=%s port=%llu grants=%s",
+		__entry->domain_id,
+		__print_flags(__entry->access_request, "|", _LANDLOCK_ACCESS_NET_NAMES),
+		__entry->port,
+		__print_landlock_layers(grants, _LANDLOCK_ACCESS_NET_NAMES))
+);
+
 #undef _LANDLOCK_NAME_ENTRY
 
 #endif /* _TRACE_LANDLOCK_H */
diff --git a/security/landlock/domain.c b/security/landlock/domain.c
index 7651eee8fe02..1e592ea52fa1 100644
--- a/security/landlock/domain.c
+++ b/security/landlock/domain.c
@@ -97,9 +97,9 @@ void landlock_put_domain_deferred(struct landlock_domain *const domain)
 }
 
 /* The returned access has the same lifetime as the domain. */
-const struct landlock_rule *
-landlock_find_rule(const struct landlock_domain *const domain,
-		   const struct landlock_id id)
+static const struct landlock_rule *
+find_rule(const struct landlock_domain *const domain,
+	  const struct landlock_id id)
 {
 	const struct rb_root *root;
 	const struct rb_node *node;
@@ -126,26 +126,38 @@ landlock_find_rule(const struct landlock_domain *const domain,
 
 /**
  * landlock_unmask_layers - Remove the access rights in @masks which are
- *                          granted in @rule
+ *                          granted by a matching rule
  *
- * Updates the set of (per-layer) unfulfilled access rights @masks so that all
- * the access rights granted in @rule are removed from it (because they are now
- * fulfilled).
+ * Looks up the rule matching @id in @domain, then updates the set of
+ * (per-layer) unfulfilled access rights @masks so that all the access rights
+ * granted by that rule are removed (because they are now fulfilled).
  *
- * @rule: A rule that grants a set of access rights for each layer.
+ * @domain: The Landlock domain to search for a matching rule.
+ * @id: Identifier for the rule target (e.g. inode, port).
  * @masks: A matrix of unfulfilled access rights for each layer.
+ * @matched_rule: Optional output for the matched rule (for tracing); set to
+ *                the matching rule when non-NULL, unchanged otherwise.
  *
  * Return: True if the request is allowed (i.e. the access rights granted all
  * remaining unfulfilled access rights and masks has no leftover set bits).
  */
-bool landlock_unmask_layers(const struct landlock_rule *const rule,
-			    struct layer_masks *masks)
+bool landlock_unmask_layers(const struct landlock_domain *const domain,
+			    const struct landlock_id id,
+			    struct layer_masks *masks,
+			    const struct landlock_rule **matched_rule)
 {
+	const struct landlock_rule *rule;
+
 	if (!masks)
 		return true;
+
+	rule = find_rule(domain, id);
 	if (!rule)
 		return false;
 
+	if (matched_rule)
+		*matched_rule = rule;
+
 	/*
 	 * An access is granted if, for each policy layer, at least one rule
 	 * encountered on the pathwalk grants the requested access, regardless
diff --git a/security/landlock/domain.h b/security/landlock/domain.h
index 8351e22016fe..5baa4a73b446 100644
--- a/security/landlock/domain.h
+++ b/security/landlock/domain.h
@@ -305,12 +305,10 @@ struct landlock_domain *
 landlock_merge_ruleset(struct landlock_domain *const parent,
 		       struct landlock_ruleset *const ruleset);
 
-const struct landlock_rule *
-landlock_find_rule(const struct landlock_domain *const domain,
-		   const struct landlock_id id);
-
-bool landlock_unmask_layers(const struct landlock_rule *const rule,
-			    struct layer_masks *masks);
+bool landlock_unmask_layers(const struct landlock_domain *const domain,
+			    const struct landlock_id id,
+			    struct layer_masks *masks,
+			    const struct landlock_rule **matched_rule);
 
 access_mask_t
 landlock_init_layer_masks(const struct landlock_domain *const domain,
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 3a33e7393e25..30aa6ce13590 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -377,31 +377,55 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
 
 /* Access-control management */
 
-/*
- * The lifetime of the returned rule is tied to @domain.
+/**
+ * get_inode_id - Look up the Landlock object for a dentry
+ * @dentry: The dentry to look up.
+ * @id: Filled with the inode's Landlock object pointer on success.
+ *
+ * Extracts the Landlock object pointer from @dentry's inode security blob and
+ * stores it in @id for use as a rule-tree lookup key.
  *
- * Returns NULL if no rule is found or if @dentry is negative.
+ * When this returns false (negative dentry or no Landlock object), no rule can
+ * match this inode, so landlock_unmask_layers() need not be called.  Callers
+ * that gate landlock_unmask_layers() on this function must handle the NULL
+ * masks case independently, since the !masks-returns-true early-return in
+ * landlock_unmask_layers() will not be reached.  See the allowed_parent2
+ * initialization in is_access_to_paths_allowed().
+ *
+ * Return: True if a Landlock object exists for @dentry, false otherwise.
  */
-static const struct landlock_rule *
-find_rule(const struct landlock_domain *const domain,
-	  const struct dentry *const dentry)
+static bool get_inode_id(const struct dentry *const dentry,
+			 struct landlock_id *id)
 {
-	const struct landlock_rule *rule;
-	const struct inode *inode;
-	struct landlock_id id = {
-		.type = LANDLOCK_KEY_INODE,
-	};
-
 	/* Ignores nonexistent leafs. */
 	if (d_is_negative(dentry))
-		return NULL;
+		return false;
 
-	inode = d_backing_inode(dentry);
-	rcu_read_lock();
-	id.key.object = rcu_dereference(landlock_inode(inode)->object);
-	rule = landlock_find_rule(domain, id);
-	rcu_read_unlock();
-	return rule;
+	/*
+	 * rcu_access_pointer() is sufficient: the pointer is used only as a
+	 * numeric comparison key for rule lookup, not dereferenced.  The object
+	 * cannot be freed while the domain exists because the domain's rule
+	 * tree holds its own reference to it.
+	 */
+	id->key.object = rcu_access_pointer(
+		landlock_inode(d_backing_inode(dentry))->object);
+	return !!id->key.object;
+}
+
+static bool unmask_layers_fs(const struct landlock_domain *const domain,
+			     const struct landlock_id id,
+			     const access_mask_t access_request,
+			     struct layer_masks *masks,
+			     const struct dentry *const dentry)
+{
+	const struct landlock_rule *rule = NULL;
+	bool ret;
+
+	ret = landlock_unmask_layers(domain, id, masks, &rule);
+	if (rule)
+		trace_landlock_check_rule_fs(domain, rule, access_request,
+					     dentry);
+	return ret;
 }
 
 /*
@@ -782,6 +806,9 @@ is_access_to_paths_allowed(const struct landlock_domain *const domain,
 	bool allowed_parent1 = false, allowed_parent2 = false, is_dom_check,
 	     child1_is_directory = true, child2_is_directory = true;
 	struct path walker_path;
+	struct landlock_id id = {
+		.type = LANDLOCK_KEY_INODE,
+	};
 	access_mask_t access_masked_parent1, access_masked_parent2;
 	struct layer_masks _layer_masks_child1, _layer_masks_child2;
 	struct layer_masks *layer_masks_child1 = NULL,
@@ -821,28 +848,46 @@ is_access_to_paths_allowed(const struct landlock_domain *const domain,
 		/* For a simple request, only check for requested accesses. */
 		access_masked_parent1 = access_request_parent1;
 		access_masked_parent2 = access_request_parent2;
+		/*
+		 * Simple requests have no parent2 to check, so parent2 is
+		 * trivially allowed.  This must be set explicitly because the
+		 * get_inode_id() gate in the pathwalk loop may prevent
+		 * landlock_unmask_layers() from being called (which would
+		 * otherwise return true for NULL masks as a side effect).
+		 */
+		allowed_parent2 = true;
 		is_dom_check = false;
 	}
 
 	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))
-			landlock_unmask_layers(find_rule(domain, dentry_child1),
-					       &_layer_masks_child1);
+		struct landlock_id id = {
+			.type = LANDLOCK_KEY_INODE,
+		};
+		access_mask_t handled;
+
+		handled = landlock_init_layer_masks(domain,
+						    LANDLOCK_MASK_ACCESS_FS,
+						    &_layer_masks_child1,
+						    LANDLOCK_KEY_INODE);
+		if (handled && get_inode_id(dentry_child1, &id))
+			unmask_layers_fs(domain, id, handled,
+					 &_layer_masks_child1, dentry_child1);
 		layer_masks_child1 = &_layer_masks_child1;
 		child1_is_directory = d_is_dir(dentry_child1);
 	}
 	if (unlikely(dentry_child2)) {
-		if (landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
-					      &_layer_masks_child2,
-					      LANDLOCK_KEY_INODE))
-			landlock_unmask_layers(find_rule(domain, dentry_child2),
-					       &_layer_masks_child2);
+		struct landlock_id id = {
+			.type = LANDLOCK_KEY_INODE,
+		};
+		access_mask_t handled;
+
+		handled = landlock_init_layer_masks(domain,
+						    LANDLOCK_MASK_ACCESS_FS,
+						    &_layer_masks_child2,
+						    LANDLOCK_KEY_INODE);
+		if (handled && get_inode_id(dentry_child2, &id))
+			unmask_layers_fs(domain, id, handled,
+					 &_layer_masks_child2, dentry_child2);
 		layer_masks_child2 = &_layer_masks_child2;
 		child2_is_directory = d_is_dir(dentry_child2);
 	}
@@ -854,8 +899,6 @@ is_access_to_paths_allowed(const struct landlock_domain *const domain,
 	 * restriction.
 	 */
 	while (true) {
-		const struct landlock_rule *rule;
-
 		/*
 		 * If at least all accesses allowed on the destination are
 		 * already allowed on the source, respectively if there is at
@@ -896,13 +939,20 @@ is_access_to_paths_allowed(const struct landlock_domain *const domain,
 				break;
 		}
 
-		rule = find_rule(domain, walker_path.dentry);
-		allowed_parent1 =
-			allowed_parent1 ||
-			landlock_unmask_layers(rule, layer_masks_parent1);
-		allowed_parent2 =
-			allowed_parent2 ||
-			landlock_unmask_layers(rule, layer_masks_parent2);
+		if (get_inode_id(walker_path.dentry, &id)) {
+			allowed_parent1 =
+				allowed_parent1 ||
+				unmask_layers_fs(domain, id,
+						 access_masked_parent1,
+						 layer_masks_parent1,
+						 walker_path.dentry);
+			allowed_parent2 =
+				allowed_parent2 ||
+				unmask_layers_fs(domain, id,
+						 access_masked_parent2,
+						 layer_masks_parent2,
+						 walker_path.dentry);
+		}
 
 		/* Stops when a rule from each layer grants access. */
 		if (allowed_parent1 && allowed_parent2)
@@ -1076,23 +1126,30 @@ static bool collect_domain_accesses(const struct landlock_domain *const domain,
 				    struct layer_masks *layer_masks_dom)
 {
 	bool ret = false;
+	access_mask_t access_masked_dom;
 
 	if (WARN_ON_ONCE(!domain || !mnt_root || !dir || !layer_masks_dom))
 		return true;
 	if (is_nouser_or_private(dir))
 		return true;
 
-	if (!landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
-				       layer_masks_dom, LANDLOCK_KEY_INODE))
+	access_masked_dom =
+		landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
+					  layer_masks_dom, LANDLOCK_KEY_INODE);
+	if (!access_masked_dom)
 		return true;
 
 	dget(dir);
 	while (true) {
 		struct dentry *parent_dentry;
+		struct landlock_id id = {
+			.type = LANDLOCK_KEY_INODE,
+		};
 
 		/* Gets all layers allowing all domain accesses. */
-		if (landlock_unmask_layers(find_rule(domain, dir),
-					   layer_masks_dom)) {
+		if (get_inode_id(dir, &id) &&
+		    unmask_layers_fs(domain, id, access_masked_dom,
+				     layer_masks_dom, dir)) {
 			/*
 			 * Stops when all handled accesses are allowed by at
 			 * least one rule in each layer.
diff --git a/security/landlock/net.c b/security/landlock/net.c
index ead97fcfdcff..8f2aaac54b33 100644
--- a/security/landlock/net.c
+++ b/security/landlock/net.c
@@ -53,6 +53,22 @@ int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
 	return err;
 }
 
+static bool unmask_layers_net(const struct landlock_domain *const domain,
+			      const struct landlock_id id,
+			      struct layer_masks *masks,
+			      access_mask_t access_request)
+{
+	const struct landlock_rule *rule = NULL;
+	bool ret;
+
+	ret = landlock_unmask_layers(domain, id, masks, &rule);
+	if (rule)
+		trace_landlock_check_rule_net(
+			domain, rule, access_request,
+			ntohs((__force __be16)id.key.data));
+	return ret;
+}
+
 static int current_check_access_socket(struct socket *const sock,
 				       struct sockaddr *const address,
 				       const int addrlen,
@@ -62,7 +78,6 @@ static int current_check_access_socket(struct socket *const sock,
 	unsigned short sock_family;
 	__be16 port;
 	struct layer_masks layer_masks = {};
-	const struct landlock_rule *rule;
 	struct landlock_id id = {
 		.type = LANDLOCK_KEY_NET_PORT,
 	};
@@ -248,14 +263,14 @@ static int current_check_access_socket(struct socket *const sock,
 	id.key.data = (__force uintptr_t)port;
 	BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
 
-	rule = landlock_find_rule(subject->domain, id);
 	access_request = landlock_init_layer_masks(subject->domain,
 						   access_request, &layer_masks,
 						   LANDLOCK_KEY_NET_PORT);
 	if (!access_request)
 		return 0;
 
-	if (landlock_unmask_layers(rule, &layer_masks))
+	if (unmask_layers_net(subject->domain, id, &layer_masks,
+			      access_request))
 		return 0;
 
 	audit_net.family = address->sa_family;
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.