Linux Trace Kernel
 help / color / mirror / Atom feed
From: "Mickaël Salaün" <mic@digikod.net>
To: "Günther Noack" <gnoack@google.com>,
	"Steven Rostedt" <rostedt@goodmis.org>
Cc: "Mickaël Salaün" <mic@digikod.net>,
	"Christian Brauner" <brauner@kernel.org>,
	"Jann Horn" <jannh@google.com>, "Jeff Xu" <jeffxu@google.com>,
	"Justin Suess" <utilityemal77@gmail.com>,
	"Kees Cook" <kees@kernel.org>,
	"Masami Hiramatsu" <mhiramat@kernel.org>,
	"Mathieu Desnoyers" <mathieu.desnoyers@efficios.com>,
	"Matthieu Buffet" <matthieu@buffet.re>,
	"Mikhail Ivanov" <ivanov.mikhail1@huawei-partners.com>,
	"Tingmao Wang" <m@maowtm.org>,
	kernel-team@cloudflare.com,
	linux-security-module@vger.kernel.org,
	linux-trace-kernel@vger.kernel.org
Subject: [PATCH v3 11/20] landlock: Add landlock_enforce_domain tracepoint
Date: Wed, 22 Jul 2026 19:11:43 +0200	[thread overview]
Message-ID: <20260722171159.2776765-12-mic@digikod.net> (raw)
In-Reply-To: <20260722171159.2776765-1-mic@digikod.net>

The landlock_create_domain event records that a domain was created,
once, before thread-sync.  It cannot tell which threads end up enforcing
it: a successful landlock_restrict_self(2) with
LANDLOCK_RESTRICT_SELF_TSYNC applies the domain to the caller and every
eligible sibling.  Creation (the operation) and enforcement (the
per-thread outcome) are distinct.

Add landlock_enforce_domain(domain, complete, process_wide), emitted
once per thread the domain is applied to, strictly after that thread's
commit_creds(), so it fires only for a thread that is enforcing the
domain, never speculatively; an aborted operation emits none.  The
lifecycle now reads create -> enforce* -> free.

The two booleans name properties, not the implementation:
- complete: marks the single event that concludes the operation.  It
  names the outcome, the set is now enforced, not which thread
  finishes, which the contract leaves unspecified.
- process_wide: means every eligible thread of the process is
  covered.  It is set race-free by either establishing path,
  thread-sync or a single-threaded process, so
  complete && process_wide is the whole-process-enforced guarantee.

The requesting thread and source ruleset are not repeated here: they are
on create_domain (joined via domain->hierarchy->id) and on the immutable
domain->hierarchy->details.  Source ruleset means the ruleset_id and
ruleset_version recorded on create_domain, not the ruleset object, which
the caller may close before enforcement.

Cc: Günther Noack <gnoack@google.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Tingmao Wang <m@maowtm.org>
Signed-off-by: Mickaël Salaün <mic@digikod.net>
---

Changes since v2:
- New patch.
---
 include/trace/events/landlock.h | 52 +++++++++++++++++++++++++++++++++
 security/landlock/syscalls.c    | 13 ++++++++-
 security/landlock/tsync.c       | 15 ++++++++++
 3 files changed, 79 insertions(+), 1 deletion(-)

diff --git a/include/trace/events/landlock.h b/include/trace/events/landlock.h
index cb0e21a2fa1f..7f221d8fff38 100644
--- a/include/trace/events/landlock.h
+++ b/include/trace/events/landlock.h
@@ -289,6 +289,58 @@ TRACE_EVENT(landlock_create_domain,
 		__entry->ruleset_id, __entry->ruleset_version)
 );
 
+/**
+ * landlock_enforce_domain - Domain enforced on a thread
+ *
+ * @domain: Domain now enforced on the current thread (never NULL,
+ *          immutable; read locklessly).  Correlate to
+ *          landlock_create_domain via @domain->hierarchy->id for the
+ *          source ruleset and requesting thread, or read
+ *          @domain->hierarchy->details for the requesting process.
+ * @complete: Set on the single event that concludes the operation, after
+ *            all its other enforcements; filter on it for one event per
+ *            operation.
+ * @process_wide: The enforcement covers every eligible (non-exiting)
+ *                thread of the process: set when the caller used
+ *                %LANDLOCK_RESTRICT_SELF_TSYNC or the process is
+ *                single-threaded.  A lone thread whose group still
+ *                holds a zombie leader is not counted single-threaded,
+ *                so process_wide == 0 never proves the opposite.
+ *
+ * Emitted for each thread sys_landlock_restrict_self() enforces the
+ * domain on, in that thread's own context, right after its
+ * commit_creds(), so it fires only once the thread is irreversibly
+ * enforcing the domain (aborted operations emit none).  Not
+ * balanced; every enforcement falls between the domain's
+ * landlock_create_domain and landlock_free_domain events.
+ *
+ * @complete == 1 && @process_wide == 1 means the whole process is
+ * sandboxed by @domain, durably (Landlock domains are monotonic and
+ * inherited on :manpage:`clone(2)`).
+ */
+TRACE_EVENT(landlock_enforce_domain,
+
+	TP_PROTO(const struct landlock_domain *domain, bool complete,
+		 bool process_wide),
+
+	TP_ARGS(domain, complete, process_wide),
+
+	TP_STRUCT__entry(
+		__field(	__u64,		domain_id	)
+		__field(	bool,		complete	)
+		__field(	bool,		process_wide	)
+	),
+
+	TP_fast_assign(
+		__entry->domain_id	= domain->hierarchy->id;
+		__entry->complete	= complete;
+		__entry->process_wide	= process_wide;
+	),
+
+	TP_printk("domain=%llx complete=%d process_wide=%d",
+		__entry->domain_id, __entry->complete, __entry->process_wide)
+);
+
 /**
  * landlock_free_domain - Domain freed
  *
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 7a1ec140cf14..bf838f65b060 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -22,6 +22,7 @@
 #include <linux/mount.h>
 #include <linux/path.h>
 #include <linux/sched.h>
+#include <linux/sched/signal.h>
 #include <linux/security.h>
 #include <linux/stddef.h>
 #include <linux/syscalls.h>
@@ -539,6 +540,7 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
 	struct landlock_domain *new_dom = NULL;
 	struct cred *new_cred;
 	struct landlock_cred_security *new_llcred;
+	bool process_wide;
 	bool __maybe_unused log_same_exec, log_new_exec, log_subdomains,
 		prev_log_subdomains;
 
@@ -677,5 +679,14 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
 		}
 	}
 
-	return commit_creds(new_cred);
+	/* Whole process: thread-sync swept siblings, or single-threaded. */
+	process_wide = (flags & LANDLOCK_RESTRICT_SELF_TSYNC) ||
+		       get_nr_threads(current) == 1;
+	commit_creds(new_cred);
+
+	/* The caller commits last, so its event concludes the operation. */
+	if (ruleset)
+		trace_landlock_enforce_domain(new_dom, true, process_wide);
+
+	return 0;
 }
diff --git a/security/landlock/tsync.c b/security/landlock/tsync.c
index c5730bbd9ed3..b7de4f9624c7 100644
--- a/security/landlock/tsync.c
+++ b/security/landlock/tsync.c
@@ -21,6 +21,8 @@
 #include "cred.h"
 #include "tsync.h"
 
+#include <trace/events/landlock.h>
+
 /*
  * Shared state between multiple threads which are enforcing Landlock rulesets
  * in lockstep with each other.
@@ -78,6 +80,8 @@ struct tsync_work {
  */
 static void restrict_one_thread(struct tsync_shared_context *ctx)
 {
+	const struct landlock_domain *new_dom =
+		landlock_cred(ctx->new_cred)->domain;
 	int err;
 	struct cred *cred = NULL;
 
@@ -146,6 +150,17 @@ static void restrict_one_thread(struct tsync_shared_context *ctx)
 
 	commit_creds(cred);
 
+	/*
+	 * Emitted strictly after commit_creds() and before the out: label, so
+	 * it fires only for a thread now enforcing new_dom, and every
+	 * non-concluding (complete == false) event happens-before the
+	 * operation's single concluding one.  Skipped on the flags-only path,
+	 * where old_cred and new_cred carry the same domain.  A sibling never
+	 * concludes the operation and its enforcement is always process-wide.
+	 */
+	if (new_dom != landlock_cred(ctx->old_cred)->domain)
+		trace_landlock_enforce_domain(new_dom, false, true);
+
 out:
 	/* Notify the calling thread once all threads are done */
 	if (atomic_dec_return(&ctx->num_unfinished) == 0)
-- 
2.54.0


  parent reply	other threads:[~2026-07-22 17:12 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-22 17:11 [PATCH v3 00/20] Landlock tracepoints Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 01/20] landlock: Prepare ruleset and domain type split Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 02/20] landlock: Move domain query functions to domain.c Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 03/20] landlock: Split struct landlock_domain from struct landlock_ruleset Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 04/20] landlock: Split denial logging from audit into common framework Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 05/20] landlock: Decouple the per-denial logging decision from CONFIG_AUDIT Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 06/20] landlock: Consolidate access-right and scope names in a shared header Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 07/20] tracing: Add __print_untrusted_str() Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 08/20] landlock: Add create_ruleset and free_ruleset tracepoints Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 09/20] landlock: Add landlock_add_rule_fs and landlock_add_rule_net tracepoints Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 10/20] landlock: Add create_domain and free_domain tracepoints Mickaël Salaün
2026-07-22 17:11 ` Mickaël Salaün [this message]
2026-07-22 17:11 ` [PATCH v3 12/20] landlock: Add tracepoints for rule checking Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 13/20] landlock: Add landlock_deny_access_fs and landlock_deny_access_net Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 14/20] landlock: Add tracepoints for ptrace and scope denials Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 15/20] selftests/landlock: Add trace event test infrastructure and tests Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 16/20] selftests/landlock: Add filesystem tracepoint tests Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 17/20] selftests/landlock: Add network " Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 18/20] selftests/landlock: Add scope and ptrace " Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 19/20] selftests/landlock: Add landlock_enforce_domain trace tests Mickaël Salaün
2026-07-22 17:11 ` [PATCH v3 20/20] landlock: Document tracepoints 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=20260722171159.2776765-12-mic@digikod.net \
    --to=mic@digikod.net \
    --cc=brauner@kernel.org \
    --cc=gnoack@google.com \
    --cc=ivanov.mikhail1@huawei-partners.com \
    --cc=jannh@google.com \
    --cc=jeffxu@google.com \
    --cc=kees@kernel.org \
    --cc=kernel-team@cloudflare.com \
    --cc=linux-security-module@vger.kernel.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=m@maowtm.org \
    --cc=mathieu.desnoyers@efficios.com \
    --cc=matthieu@buffet.re \
    --cc=mhiramat@kernel.org \
    --cc=rostedt@goodmis.org \
    --cc=utilityemal77@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