The signal-scope denial callback identifies its target but not the
effective signal. This loses permission-probe signal zero and makes the
file-owner hook's zero sentinel ambiguous.

Append an int signal argument to the typed-BPF callback. Preserve sig,
including zero, in hook_task_kill(). In hook_file_send_sigiotask(),
translate signum zero to SIGIO at the producer, where its meaning is
known.

Carry the effective signal and target domain ID in a private,
stack-backed context consumed synchronously. This requires no allocation
or task reference in the interrupt-capable file-owner path. Gate this
context and the remaining scope-only domain IDs with CONFIG_TRACEPOINTS.

Keep the tracefs record and audit output unchanged.

Cc: Günther Noack <[email protected]>
Cc: Steven Rostedt <[email protected]>
Fixes: bb91730f16c0 ("landlock: Add tracepoints for ptrace and scope denials")
Signed-off-by: Mickaël Salaün <[email protected]>
---
 include/trace/events/landlock.h |  8 +++--
 security/landlock/log.h         | 21 ++++++++----
 security/landlock/task.c        | 59 +++++++++++++++++++++++----------
 security/landlock/trace.c       | 12 +++++--
 4 files changed, 71 insertions(+), 29 deletions(-)

diff --git a/include/trace/events/landlock.h b/include/trace/events/landlock.h
index 8c6ebf958d66..5da0f12ed2c3 100644
--- a/include/trace/events/landlock.h
+++ b/include/trace/events/landlock.h
@@ -928,12 +928,14 @@ TRACE_EVENT(landlock_deny_ptrace,
  *
  * @hierarchy: Denying domain's hierarchy node (never NULL); its id is the
  *             domain field.
- * @same_exec: Whether the current task entered the denying domain itself.
+ * @same_exec: Whether the policy subject entered the denying domain itself.
  * @logged: The domain's audit-logging decision for this denial.
  * @target_domain_id: The target's Landlock domain ID, or 0 if the target
  *                    is unsandboxed.
  * @target: The task the signal was aimed at (never NULL).  target_pid is
  *          the init-namespace TGID (like audit's opid).
+ * @signal: The signal selected by the denied check.  Zero is a permission
+ *          probe, not an absent value.
  *
  * Emitted when a Landlock domain denies signal delivery to a scoped-out
  * target.
@@ -942,9 +944,9 @@ TRACE_EVENT(landlock_deny_scope_signal,
 
        TP_PROTO(const struct landlock_hierarchy *hierarchy, bool same_exec,
                 bool logged, u64 target_domain_id,
-                const struct task_struct *target),
+                const struct task_struct *target, int signal),
 
-       TP_ARGS(hierarchy, same_exec, logged, target_domain_id, target),
+       TP_ARGS(hierarchy, same_exec, logged, target_domain_id, target, signal),
 
        TP_STRUCT__entry(
                __field(        u64,            domain_id       )
diff --git a/security/landlock/log.h b/security/landlock/log.h
index 821df6f711c6..faa30e26e42a 100644
--- a/security/landlock/log.h
+++ b/security/landlock/log.h
@@ -45,6 +45,11 @@ struct landlock_ptrace_trace {
        const struct task_struct *tracer;
 };
 
+struct landlock_signal_trace {
+       u64 target_domain_id;
+       int signal;
+};
+
 #endif /* CONFIG_TRACEPOINTS */
 
 /*
@@ -74,22 +79,24 @@ struct landlock_request {
        deny_masks_t deny_masks;
        optional_access_t quiet_optional_accesses;
 
+#ifdef CONFIG_TRACEPOINTS
        union {
                /*
-                * Other-party domain ID for a scope denial, or 0 if that party
-                * is unsandboxed.  Store an ID, not a pointer: the other task
-                * can replace its credential and free the domain it referenced.
-                * Audit ignores this trace-only field.
+                * Other-party domain ID for an abstract UNIX socket scope
+                * denial, or 0 if that party is unsandboxed.  Store an ID, not
+                * a pointer: the other task can replace its credential and free
+                * the domain it referenced.
                 */
                u64 other_domain_id;
 
-#ifdef CONFIG_TRACEPOINTS
                /* Synchronous context for a network denial. */
                const struct landlock_net_trace *trace_net;
-               /* Consumed only by the synchronous trace dispatcher. */
+               /* Synchronous context for a ptrace denial. */
                const struct landlock_ptrace_trace *trace_ptrace;
-#endif /* CONFIG_TRACEPOINTS */
+               /* Synchronous context for a signal denial. */
+               const struct landlock_signal_trace *trace_signal;
        };
+#endif /* CONFIG_TRACEPOINTS */
 };
 
 #ifdef CONFIG_SECURITY_LANDLOCK_LOG
diff --git a/security/landlock/task.c b/security/landlock/task.c
index 445f6b921a87..d9eae86fc552 100644
--- a/security/landlock/task.c
+++ b/security/landlock/task.c
@@ -256,8 +256,7 @@ static bool domain_is_scoped(const struct landlock_domain 
*const client,
 }
 
 static bool sock_is_scoped(struct sock *const other,
-                          const struct landlock_domain *const domain,
-                          u64 *const peer_domain_id)
+                          const struct landlock_domain *const domain)
 {
        const struct landlock_domain *dom_other;
 
@@ -275,13 +274,23 @@ static bool sock_is_scoped(struct sock *const other,
                return false;
 
        dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain;
-#ifdef CONFIG_SECURITY_LANDLOCK_LOG
-       *peer_domain_id = dom_other ? dom_other->hierarchy->id : 0;
-#endif /* CONFIG_SECURITY_LANDLOCK_LOG */
        return domain_is_scoped(domain, dom_other,
                                LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
 }
 
+#ifdef CONFIG_TRACEPOINTS
+
+static u64 get_socket_domain_id(const struct sock *const other)
+{
+       const struct landlock_domain *domain;
+
+       lockdep_assert_held(&unix_sk(other)->lock);
+       domain = landlock_cred(other->sk_socket->file->f_cred)->domain;
+       return domain ? domain->hierarchy->id : 0;
+}
+
+#endif /* CONFIG_TRACEPOINTS */
+
 static bool is_abstract_socket(struct sock *const sock)
 {
        struct unix_address *addr = unix_sk(sock)->addr;
@@ -305,7 +314,6 @@ static int hook_unix_stream_connect(struct sock *const sock,
                                    struct sock *const newsk)
 {
        size_t handle_layer;
-       u64 peer_domain_id = 0;
        const struct landlock_cred_security *const subject =
                landlock_get_applicable_subject(current_cred(), unix_scope,
                                                &handle_layer);
@@ -317,7 +325,7 @@ static int hook_unix_stream_connect(struct sock *const sock,
        if (!is_abstract_socket(other))
                return 0;
 
-       if (!sock_is_scoped(other, subject->domain, &peer_domain_id))
+       if (!sock_is_scoped(other, subject->domain))
                return 0;
 
        landlock_log_denial(subject, &(struct landlock_request) {
@@ -329,7 +337,9 @@ static int hook_unix_stream_connect(struct sock *const sock,
                        },
                },
                .layer_plus_one = handle_layer + 1,
-               .other_domain_id = peer_domain_id,
+#ifdef CONFIG_TRACEPOINTS
+               .other_domain_id = get_socket_domain_id(other),
+#endif /* CONFIG_TRACEPOINTS */
        });
        return -EPERM;
 }
@@ -338,7 +348,6 @@ static int hook_unix_may_send(struct socket *const sock,
                              struct socket *const other)
 {
        size_t handle_layer;
-       u64 peer_domain_id = 0;
        const struct landlock_cred_security *const subject =
                landlock_get_applicable_subject(current_cred(), unix_scope,
                                                &handle_layer);
@@ -356,7 +365,7 @@ static int hook_unix_may_send(struct socket *const sock,
        if (!is_abstract_socket(other->sk))
                return 0;
 
-       if (!sock_is_scoped(other->sk, subject->domain, &peer_domain_id))
+       if (!sock_is_scoped(other->sk, subject->domain))
                return 0;
 
        landlock_log_denial(subject, &(struct landlock_request) {
@@ -368,7 +377,9 @@ static int hook_unix_may_send(struct socket *const sock,
                        },
                },
                .layer_plus_one = handle_layer + 1,
-               .other_domain_id = peer_domain_id,
+#ifdef CONFIG_TRACEPOINTS
+               .other_domain_id = get_socket_domain_id(other->sk),
+#endif /* CONFIG_TRACEPOINTS */
        });
        return -EPERM;
 }
@@ -383,7 +394,9 @@ static int hook_task_kill(struct task_struct *const p,
 {
        bool is_scoped;
        size_t handle_layer;
+#ifdef CONFIG_TRACEPOINTS
        u64 target_domain_id = 0;
+#endif /* CONFIG_TRACEPOINTS */
        const struct landlock_cred_security *subject;
 
        if (!cred) {
@@ -415,10 +428,10 @@ static int hook_task_kill(struct task_struct *const p,
 
                is_scoped = domain_is_scoped(subject->domain, other,
                                             signal_scope.scope);
-#ifdef CONFIG_SECURITY_LANDLOCK_LOG
+#ifdef CONFIG_TRACEPOINTS
                if (other)
                        target_domain_id = other->hierarchy->id;
-#endif /* CONFIG_SECURITY_LANDLOCK_LOG */
+#endif /* CONFIG_TRACEPOINTS */
        }
 
        if (!is_scoped)
@@ -431,7 +444,12 @@ static int hook_task_kill(struct task_struct *const p,
                        .u.tsk = p,
                },
                .layer_plus_one = handle_layer + 1,
-               .other_domain_id = target_domain_id,
+#ifdef CONFIG_TRACEPOINTS
+               .trace_signal = &(struct landlock_signal_trace) {
+                       .target_domain_id = target_domain_id,
+                       .signal = sig,
+               },
+#endif /* CONFIG_TRACEPOINTS */
        });
        return -EPERM;
 }
@@ -441,7 +459,9 @@ static int hook_file_send_sigiotask(struct task_struct *tsk,
 {
        const struct landlock_cred_security *subject;
        bool is_scoped = false;
+#ifdef CONFIG_TRACEPOINTS
        u64 target_domain_id = 0;
+#endif /* CONFIG_TRACEPOINTS */
 
        /* Lock already held by send_sigio() and send_sigurg(). */
        lockdep_assert_held(&fown->lock);
@@ -474,10 +494,10 @@ static int hook_file_send_sigiotask(struct task_struct 
*tsk,
 
                is_scoped = domain_is_scoped(subject->domain, other,
                                             signal_scope.scope);
-#ifdef CONFIG_SECURITY_LANDLOCK_LOG
+#ifdef CONFIG_TRACEPOINTS
                if (other)
                        target_domain_id = other->hierarchy->id;
-#endif /* CONFIG_SECURITY_LANDLOCK_LOG */
+#endif /* CONFIG_TRACEPOINTS */
        }
 
        if (!is_scoped)
@@ -492,7 +512,12 @@ static int hook_file_send_sigiotask(struct task_struct 
*tsk,
 #ifdef CONFIG_SECURITY_LANDLOCK_LOG
                .layer_plus_one = landlock_file(fown->file)->fown_layer + 1,
 #endif /* CONFIG_SECURITY_LANDLOCK_LOG */
-               .other_domain_id = target_domain_id,
+#ifdef CONFIG_TRACEPOINTS
+               .trace_signal = &(struct landlock_signal_trace) {
+                       .target_domain_id = target_domain_id,
+                       .signal = signum ? signum : SIGIO,
+               },
+#endif /* CONFIG_TRACEPOINTS */
        });
        return -EPERM;
 }
diff --git a/security/landlock/trace.c b/security/landlock/trace.c
index 43091c052f77..225dbf37bab0 100644
--- a/security/landlock/trace.c
+++ b/security/landlock/trace.c
@@ -201,10 +201,18 @@ void landlock_trace_denial(
                }
                break;
        case LANDLOCK_REQUEST_SCOPE_SIGNAL:
-               if (trace_landlock_deny_scope_signal_enabled())
+               if (trace_landlock_deny_scope_signal_enabled()) {
+                       const struct landlock_signal_trace *const trace_signal =
+                               request->trace_signal;
+
+                       if (WARN_ON_ONCE(!trace_signal))
+                               return;
+
                        trace_landlock_deny_scope_signal(
                                youngest_denied, same_exec, logged,
-                               request->other_domain_id, request->audit.u.tsk);
+                               trace_signal->target_domain_id,
+                               request->audit.u.tsk, trace_signal->signal);
+               }
                break;
        case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET:
                if (trace_landlock_deny_scope_abstract_unix_socket_enabled())
-- 
2.55.0


Reply via email to