On Tue, Jul 28, 2026 at 10:56:16PM +0200, Mickaël Salaün wrote: > On Tue, May 26, 2026 at 05:34:59PM -0400, Justin Suess wrote: > > On Mon, Apr 06, 2026 at 04:37:04PM +0200, Mickaël Salaün wrote: > > > Add tracepoints for ruleset lifecycle events: landlock_create_ruleset > > > fires from the landlock_create_ruleset() syscall handler, logging the > > > ruleset Landlock ID and handled access masks; landlock_free_ruleset > > > fires in free_ruleset() before the ruleset is freed, so eBPF programs > > > can access the full ruleset state via BTF. > > > > > > The create_ruleset TP_PROTO takes only the ruleset pointer. The handled > > > access masks are read from the ruleset in TP_fast_assign rather than > > > passed as scalar arguments, so eBPF programs can access the full ruleset > > > state (rules, access masks) via BTF on a single pointer. No lock is > > > needed because the ruleset is not yet shared (the file descriptor has > > > not been installed). > > > > > > Create the trace header with a DOC comment documenting the consistency > > > guarantees, locking conventions, TP_PROTO safety, and security > > > considerations shared by all Landlock tracepoints. Add > > > CREATE_TRACE_POINTS in log.c to generate the tracepoint implementations. > > > > > > Add an id field to struct landlock_ruleset, assigned from > > > landlock_get_id_range() at creation time. Extend the CONFIG guard on > > > landlock_get_id_range() from CONFIG_AUDIT to > > > CONFIG_SECURITY_LANDLOCK_LOG so that IDs are available for tracing even > > > without audit support. > > > > > > The deallocation events use the "free_" prefix (rather than "drop_") > > > because they fire when the object is actually freed. There is no need > > > for allocated/deallocated symmetry because ruleset creation happens with > > > the landlock_create_ruleset tracepoint. > > > > > > landlock_create_ruleset tracepoint. > > > > > > Unlike audit records which share a record type and need a "status=" > > > field to distinguish allocation from deallocation, tracepoints provide > > > one event type per lifecycle transition, each with a type-safe TP_PROTO > > > matching the specific transition. This enables type-safe eBPF BTF > > > access and precise ftrace filtering by event name. > > > > > > 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 v1: > > > - New patch (split from the v1 add_rule_fs tracepoint patch). > > > --- > > > MAINTAINERS | 1 + > > > include/trace/events/landlock.h | 94 +++++++++++++++++++++++++++++++++ > > > security/landlock/id.h | 6 +-- > > > security/landlock/log.c | 5 ++ > > > security/landlock/ruleset.c | 8 +++ > > > security/landlock/ruleset.h | 9 ++++ > > > security/landlock/syscalls.c | 5 ++ > > > 7 files changed, 125 insertions(+), 3 deletions(-) > > > create mode 100644 include/trace/events/landlock.h > > > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > > index c3fe46d7c4bc..51104faa3951 100644 > > > --- a/MAINTAINERS > > > +++ b/MAINTAINERS > > > @@ -14389,6 +14389,7 @@ F: > > > Documentation/admin-guide/LSM/landlock.rst > > > F: Documentation/security/landlock.rst > > > F: Documentation/userspace-api/landlock.rst > > > F: fs/ioctl.c > > > +F: include/trace/events/landlock.h > > > F: include/uapi/linux/landlock.h > > > F: samples/landlock/ > > > F: security/landlock/ > > > diff --git a/include/trace/events/landlock.h > > > b/include/trace/events/landlock.h > > > new file mode 100644 > > > index 000000000000..5e847844fbf7 > > > --- /dev/null > > > +++ b/include/trace/events/landlock.h > > > @@ -0,0 +1,94 @@ > > > +/* SPDX-License-Identifier: GPL-2.0 */ > > > +/* > > > + * Copyright © 2025 Microsoft Corporation > > > + * Copyright © 2026 Cloudflare > > > + */ > > > + > > > +#undef TRACE_SYSTEM > > > +#define TRACE_SYSTEM landlock > > > + > > > +#if !defined(_TRACE_LANDLOCK_H) || defined(TRACE_HEADER_MULTI_READ) > > > +#define _TRACE_LANDLOCK_H > > > + > > > +#include <linux/tracepoint.h> > > > + > > > +struct landlock_ruleset; > > > + > > > +/** > > > + * DOC: Landlock trace events > > > + * > > > + * Consistency guarantee: every trace event corresponds to an operation > > > + * that has irrevocably succeeded. Lifecycle events fire only after > > > + * the point of no return; denial events fire only for denials that > > > + * actually happen. This guarantees that eBPF programs observing the > > > + * trace stream can build a faithful model of Landlock state without > > > + * reconciliation logic. > > > + * > > > + * Mutable object pointers in TP_PROTO (e.g., struct landlock_ruleset > > > + * for add_rule events) are passed while the caller holds the object's > > > + * lock, so that TP_fast_assign and eBPF programs reading via BTF see a > > > + * consistent snapshot. For objects that are immutable at the emission > > > + * site (e.g., a domain after creation), no lock is needed. > > > + * > > > + * All pointer arguments in TP_PROTO are guaranteed non-NULL by the > > > + * caller. eBPF programs can access these pointers via BTF for richer > > > + * introspection than the TP_STRUCT__entry fields provide. > > > + * > > > + * TP_STRUCT__entry fields serve TP_printk display only. eBPF programs > > > + * access the raw TP_PROTO arguments directly. > > > + * > > > + * Security: as for audit, Landlock trace events may expose sensitive > > > + * information about all sandboxed processes on the system. See > > > + * Documentation/admin-guide/LSM/landlock.rst for security considerations > > > + * and privilege requirements. > > > + */ > > > + > > > +/** > > > + * landlock_create_ruleset - new ruleset created > > > + * @ruleset: Newly created ruleset (never NULL); not yet shared via an > > > fd, > > > + * so no lock is needed. eBPF programs can read the full > > > ruleset > > > + * state via BTF. > > > + */ > > > +TRACE_EVENT( > > > + landlock_create_ruleset, > > > + > > > + TP_PROTO(const struct landlock_ruleset *ruleset), > > > + > > > + TP_ARGS(ruleset), > > > + > > > + TP_STRUCT__entry(__field(__u64, ruleset_id) __field(access_mask_t, > > > + handled_fs) > > > + __field(access_mask_t, handled_net) > > > + __field(access_mask_t, scoped)), > > > + > > > + TP_fast_assign(__entry->ruleset_id = ruleset->id; > > > + __entry->handled_fs = ruleset->layer.fs; > > > + __entry->handled_net = ruleset->layer.net; > > > + __entry->scoped = ruleset->layer.scope;), > > > + > > > + TP_printk("ruleset=%llx handled_fs=0x%x handled_net=0x%x scoped=0x%x", > > > + __entry->ruleset_id, __entry->handled_fs, > > > + __entry->handled_net, __entry->scoped)); > > > + > > > +/** > > > + * landlock_free_ruleset - Ruleset freed > > > + * > > > + * Emitted when a ruleset's last reference is dropped (typically when > > > + * the creating process closes the ruleset file descriptor). > > > + */ > > > +TRACE_EVENT(landlock_free_ruleset, > > > + > > > + TP_PROTO(const struct landlock_ruleset *ruleset), > > > + > > > + TP_ARGS(ruleset), > > > + > > > + TP_STRUCT__entry(__field(__u64, ruleset_id)), > > > + > > > + TP_fast_assign(__entry->ruleset_id = ruleset->id;), > > > + > > > + TP_printk("ruleset=%llx", __entry->ruleset_id)); > > > + > > > +#endif /* _TRACE_LANDLOCK_H */ > > > + > > > +/* This part must be outside protection */ > > > +#include <trace/define_trace.h> > > > diff --git a/security/landlock/id.h b/security/landlock/id.h > > > index 45dcfb9e9a8b..2a43c2b523a8 100644 > > > --- a/security/landlock/id.h > > > +++ b/security/landlock/id.h > > > @@ -8,18 +8,18 @@ > > > #ifndef _SECURITY_LANDLOCK_ID_H > > > #define _SECURITY_LANDLOCK_ID_H > > > > > > -#ifdef CONFIG_AUDIT > > > +#ifdef CONFIG_SECURITY_LANDLOCK_LOG > > > > > > void __init landlock_init_id(void); > > > > > > u64 landlock_get_id_range(size_t number_of_ids); > > > > > > -#else /* CONFIG_AUDIT */ > > > +#else /* CONFIG_SECURITY_LANDLOCK_LOG */ > > > > > > static inline void __init landlock_init_id(void) > > > { > > > } > > > > > > -#endif /* CONFIG_AUDIT */ > > > +#endif /* CONFIG_SECURITY_LANDLOCK_LOG */ > > > > > > #endif /* _SECURITY_LANDLOCK_ID_H */ > > > diff --git a/security/landlock/log.c b/security/landlock/log.c > > > index c9b506707af0..ef79e4ed0037 100644 > > > --- a/security/landlock/log.c > > > +++ b/security/landlock/log.c > > > @@ -174,6 +174,11 @@ static void audit_denial(const struct > > > landlock_cred_security *const subject, > > > > > > #endif /* CONFIG_AUDIT */ > > > > > > +#ifdef CONFIG_TRACEPOINTS > > > +#define CREATE_TRACE_POINTS > > > +#include <trace/events/landlock.h> > > > +#endif /* CONFIG_TRACEPOINTS */ > > > + > > > static struct landlock_hierarchy * > > > get_hierarchy(const struct landlock_domain *const domain, const size_t > > > layer) > > > { > > > diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c > > > index c220e0f9cf5f..0d1e3dadb318 100644 > > > --- a/security/landlock/ruleset.c > > > +++ b/security/landlock/ruleset.c > > > @@ -22,10 +22,13 @@ > > > #include <linux/spinlock.h> > > > > > > #include "access.h" > > > +#include "id.h" > > > #include "limits.h" > > > #include "object.h" > > > #include "ruleset.h" > > > > > > +#include <trace/events/landlock.h> > > > + > > > struct landlock_ruleset * > > > landlock_create_ruleset(const access_mask_t fs_access_mask, > > > const access_mask_t net_access_mask, > > > @@ -49,6 +52,10 @@ landlock_create_ruleset(const access_mask_t > > > fs_access_mask, > > > new_ruleset->rules.root_net_port = RB_ROOT; > > > #endif /* IS_ENABLED(CONFIG_INET) */ > > > > > > +#ifdef CONFIG_SECURITY_LANDLOCK_LOG > > > + new_ruleset->id = landlock_get_id_range(1); > > > +#endif /* CONFIG_SECURITY_LANDLOCK_LOG */ > > The addition of IDs to rulesets for logging makes sense. > > > > But it is limited in usefulness without some form of introspection to be > > able to correlate it to a specific userspace ruleset. > > The main goal for tracepoints is to correlate the created rule with all > the related updates with rule addition, and then the domain creation > from this ruleset. > > > > > If a program creates multiple Landlock rulesets, and wishes to trace and > > correlate which ruleset FD corresponds to the log/tracepoint, it is > > difficult when no form of introspection exists. > > That's correct, and that's the same thing for the domain IDs, for now... > > These IDs are designed to be available to unprivileged processes, so > we'll need a proper interface. Tracepoints is the first interface to > use ruleset IDs. > Makes sense. It need not be a part of this series, just something to consider later on.
Justin > > [...]
