landlock: Fix tracepoint contract documentation

The tracepoint documentation claims that denial and lifecycle events
expose every input needed to reproduce a verdict. Instead document how
denial, ruleset, and domain events identify the denying policy, checked
operation and object, and reason for denial. Direct consumers to generic
tracepoints for additional operational context.

State the reconstruction limits: IDs are boot-local, rule checks have no
request ID, and exported records may be lost or cross-CPU reordered.

Also replace the incorrect BPF_RAW_TRACEPOINT guidance with libbpf
SEC("tp_btf/...") attachment and refer consumers to the event prototypes
for callback argument layouts.

Cc: Günther Noack <gnoack@google.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Link: https://patch.msgid.link/20260918185036.608651-10-mic@digikod.net
Signed-off-by: Mickaël Salaün <mic@digikod.net>
This commit is contained in:
Mickaël Salaün 2026-09-18 20:50:32 +02:00
parent c8dcb17205
commit 3fa5aa398e
No known key found for this signature in database
GPG Key ID: E5E3D0E88C82F6D2
2 changed files with 36 additions and 30 deletions

View File

@ -141,10 +141,10 @@ in some field formats:
treated as untrusted input and escaped in the trace text output so it
cannot inject field separators or control characters.
- **Other party's domain**: A scope or ptrace denial compares the
subject's denying domain (``domain=``, always the enforcing domain and
never the current task) with the other party's domain, so these
tracepoints also report the other party's domain as a scalar ID:
- **Other party's domain**: A scope or ptrace denial compares the subject's
denying domain (``domain=``), which is the enforcing domain and not
necessarily the current task's domain, with the other party's domain.
These tracepoints also report the other party's domain as a scalar ID:
``tracee_domain=`` (ptrace), ``target_domain=`` (signal), and
``peer_domain=`` (abstract unix socket). It is ``0`` when the other
party is unsandboxed, and otherwise a domain ID that a consumer resolves
@ -231,9 +231,10 @@ contribution, not the final decision:
through another matching rule, or the right is denied and appears in the
``blockers=`` field of the corresponding ``deny_access`` event.
To reconstruct the decision for an object, aggregate the ``grants=``
groups of all ``check_rule`` events emitted for that object during the
check.
For an access check that a consumer can delimit, aggregate the ``grants=``
groups of all matching ``check_rule`` events. These events do not carry a
request ID; use their execution context and generic tracepoints to separate
concurrent or successive checks of the same object.
.. note::
@ -264,9 +265,11 @@ layers, so ``grants=`` has one group per layer::
eBPF access
===========
eBPF programs attached via ``BPF_RAW_TRACEPOINT`` can access the
tracepoint arguments directly through BTF. The arguments include both
standard kernel objects and Landlock-internal objects:
BTF-enabled raw tracepoint programs attached through libbpf
``SEC("tp_btf/...")`` sections receive typed callback arguments. The
event prototypes in `Event reference`_ document their argument layouts.
The arguments include both standard kernel objects and Landlock-internal
objects:
- Standard kernel objects (``struct task_struct``, ``struct sock``,
``struct path``, ``struct dentry``) can be used with existing BPF
@ -277,7 +280,7 @@ standard kernel objects and Landlock-internal objects:
Internal struct layouts may change between kernel versions; use CO-RE
for field relocation.
A stateful eBPF program observes the full event stream and maintains
A stateful eBPF program attached before sandbox construction can maintain
per-domain state in BPF maps:
1. On ``landlock_create_domain``: record the domain ID and parent (the
@ -293,7 +296,10 @@ per-domain state in BPF maps:
final statistics.
This approach requires no kernel modification and no Landlock-specific
BPF helpers. The Landlock IDs serve as correlation keys across events.
BPF helpers. Landlock IDs serve as correlation keys within one boot.
Records exported through tracing or BPF buffers can be lost, and records
from different CPUs are not globally ordered, so consumers must detect and
reconcile incomplete state.
Audit filtering equivalence
===========================

View File

@ -206,22 +206,22 @@ static inline const char *__trace_landlock_print_layers(
* Decision context
* ~~~~~~~~~~~~~~~~
*
* A denial event, together with the lifecycle events, exposes the full
* set of inputs the verdict consumed, so a consumer that tracked domain
* creation (landlock_create_ruleset, landlock_create_domain) can verify
* or reproduce the Landlock decision rather than merely observe it
* happened. In who/what/why terms: who is the denying domain (the domain
* field, always the subject that enforced the policy, never the current
* task), what is the operation and its object, and why is every other
* input the verdict weighed.
* A denial event identifies the domain whose policy denied the request, the
* Landlock operation and policy object that were checked, and the blocker or
* domain relationship responsible for the denial. When tracing starts with
* sandbox construction, ruleset and domain events provide the policy history
* needed to interpret these identifiers. The denying domain is the subject
* that enforced the policy, not necessarily current. Generic tracepoints can
* provide additional operational context.
*
* Lifecycle consistency
* ~~~~~~~~~~~~~~~~~~~~~~
*
* Lifecycle events are balanced: a creation event always has a matching
* deallocation event and vice versa, so an eBPF program can model object
* lifetimes from the trace stream without reconciliation logic. A creation
* event fires while the object is still private to the calling thread
* Lifecycle emission is balanced: a creation event always has a matching
* deallocation event and vice versa. A consumer that observes an object's
* complete lifetime can model it from this pair; one that attaches late or
* loses exported records must reconcile incomplete state. A creation event
* fires while the object is still private to the calling thread
* (landlock_create_ruleset fires before the ruleset's file descriptor is
* installed, so it cannot race a concurrent :manpage:`close(2)`); if fd
* installation later fails and the ruleset is freed, free_ruleset still
@ -295,7 +295,8 @@ static inline const char *__trace_landlock_print_layers(
* the two parties without kernel-internal state. The ID is a scalar
* snapshot, not a live domain pointer that could dangle: an optional
* relational referent is a scalar (0 sentinel), not a nullable pointer.
* For ptrace, same_exec instead describes the tracer, even for
* Nonzero IDs are unique within one boot. For ptrace, same_exec instead
* describes the tracer, even for
* PTRACE_TRACEME, and may differ from the current task.
*
* Blocker fields
@ -1031,11 +1032,10 @@ TRACE_EVENT(landlock_deny_scope_abstract_unix_socket,
__entry->logged = logged;
__entry->peer_domain_id = peer_domain_id;
/*
* Best-effort (0 for a datagram peer). sk_peer_pid is
* canonically guarded by sk->sk_peer_lock, but the target
* peer's peercred is set once and not updated concurrently in
* these hooks, so this READ_ONCE() is safe; sun_path is the
* reliable identifier.
* Best-effort (0 for a datagram peer). The caller holds the
* peer's AF_UNIX state lock, serializing published peercred
* updates. The peer socket keeps a reference to sk_peer_pid
* through pid_nr(); sun_path is the reliable identifier.
*/
peer_pid = READ_ONCE(peer->sk_peer_pid);
__entry->peer_pid = peer_pid ? pid_nr(peer_pid) : 0;