xref: /linux/security/landlock/trace.c (revision 2f0f6b0773be0a1ec475097ae54848eea42adc7d)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Landlock - Tracepoint helpers
4  *
5  * Copyright © 2025 Microsoft Corporation
6  * Copyright © 2026 Cloudflare, Inc.
7  */
8 
9 #include <linux/cleanup.h>
10 #include <linux/dcache.h>
11 #include <linux/err.h>
12 #include <linux/fs.h>
13 #include <linux/lsm_audit.h>
14 #include <net/sock.h>
15 
16 #include "access.h"
17 #include "domain.h"
18 #include "fs.h"
19 #include "log.h"
20 #include "ruleset.h"
21 #include "trace.h"
22 
23 /*
24  * Generates the tracepoint definitions in this translation unit.  The trace
25  * event header dereferences the traced objects in TP_fast_assign, so the full
26  * struct definitions (e.g. ruleset.h, domain.h) must be included before it.
27  */
28 #define CREATE_TRACE_POINTS
29 #include <trace/events/landlock.h>
30 
31 /**
32  * landlock_trace_free_domain - Emit a tracepoint on domain deallocation
33  *
34  * @hierarchy: The domain's hierarchy being deallocated.
35  *
36  * Fires only for a hierarchy whose creation event was emitted, i.e. one that
37  * left LANDLOCK_LOG_UNCOMMITTED in landlock_restrict_self().  This keeps the
38  * create/free pair balanced: a hierarchy that never became observable is freed
39  * silently, while a domain that landlock_restrict_self() created and a
40  * thread-sync failure then aborted still fires free_domain, because its
41  * creation event already fired.
42  *
43  * Called from landlock_log_free_domain().
44  */
45 void landlock_trace_free_domain(const struct landlock_hierarchy *const hierarchy)
46 {
47 	/*
48 	 * The log_status read is a correctness guard (keep the create/free pair
49 	 * balanced), not a cost guard, so this cold path needs no
50 	 * trace_..._enabled() check: the tracepoint is a static-branch no-op
51 	 * when disabled.  The denial path guards trace_..._enabled() instead
52 	 * because it does expensive __getname()/path work before emitting.
53 	 */
54 	if (READ_ONCE(hierarchy->log_status) != LANDLOCK_LOG_UNCOMMITTED)
55 		trace_landlock_free_domain(hierarchy);
56 }
57 
58 /**
59  * landlock_trace_denial - Emit a tracepoint for a denied access request
60  *
61  * @request: Detail of the user space request.
62  * @youngest_denied: The youngest hierarchy node that denied the access.
63  * @missing: The set of denied access rights.
64  * @same_exec: Whether the current task is the same executable that called
65  *             landlock_restrict_self() for the denying domain, as computed
66  *             by landlock_log_denial().
67  * @logged: Whether the domain's policy selects this denial for logging, as
68  *          computed by landlock_log_denial().
69  *
70  * Emits the tracepoint matching @request->type when its event is enabled.
71  * Unlike audit, fires regardless of @logged; the value is recorded in the event
72  * so consumers can filter on it.
73  *
74  * Called from landlock_log_denial().
75  */
76 void landlock_trace_denial(
77 	const struct landlock_request *const request,
78 	const struct landlock_hierarchy *const youngest_denied,
79 	const access_mask_t missing, const bool same_exec, const bool logged)
80 {
81 	switch (request->type) {
82 	case LANDLOCK_REQUEST_FS_ACCESS:
83 	case LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY:
84 		if (trace_landlock_deny_access_fs_enabled()) {
85 			char *buf __free(__putname) = __getname();
86 			struct path dentry_path;
87 			const char *pathname;
88 			const struct path *path = NULL;
89 
90 			/*
91 			 * Selects the path from the audit data type, as
92 			 * dump_common_audit_data() does.  A FS_ACCESS denial
93 			 * carries a file (hook_file_truncate) or an ioctl op
94 			 * (hook_file_ioctl) rather than a path;
95 			 * FS_CHANGE_TOPOLOGY carries a path or a bare dentry.
96 			 * Reading the wrong union member would dereference
97 			 * garbage, so every reachable type is handled here.
98 			 */
99 			switch (request->audit.type) {
100 			case LSM_AUDIT_DATA_FILE:
101 				path = &request->audit.u.file->f_path;
102 				break;
103 			case LSM_AUDIT_DATA_IOCTL_OP:
104 				path = &request->audit.u.op->path;
105 				break;
106 			case LSM_AUDIT_DATA_DENTRY:
107 				/*
108 				 * Build a path on the stack with the real
109 				 * dentry so TP_fast_assign can extract dev and
110 				 * ino; the mnt field is unused there.
111 				 */
112 				dentry_path = (struct path){
113 					.dentry = request->audit.u.dentry,
114 				};
115 				path = &dentry_path;
116 				break;
117 			case LSM_AUDIT_DATA_PATH:
118 				path = &request->audit.u.path;
119 				break;
120 			default:
121 				WARN_ONCE(1,
122 					  "Unhandled Landlock FS audit type %d",
123 					  request->audit.type);
124 				break;
125 			}
126 
127 			if (!path)
128 				break;
129 
130 			if (!buf) {
131 				pathname = "<no_mem>";
132 			} else if (request->audit.type ==
133 				   LSM_AUDIT_DATA_DENTRY) {
134 				/* No vfsmount: render the dentry path alone. */
135 				pathname = dentry_path_raw(
136 					request->audit.u.dentry, buf, PATH_MAX);
137 				if (IS_ERR(pathname))
138 					pathname =
139 						PTR_ERR(pathname) ==
140 								-ENAMETOOLONG ?
141 							"<too_long>" :
142 							"<unreachable>";
143 			} else {
144 				pathname = resolve_path_for_trace(path, buf);
145 			}
146 
147 			trace_landlock_deny_access_fs(youngest_denied,
148 						      same_exec, logged,
149 						      missing, path, pathname);
150 		}
151 		break;
152 	case LANDLOCK_REQUEST_NET_ACCESS:
153 		if (trace_landlock_deny_access_net_enabled())
154 			trace_landlock_deny_access_net(
155 				youngest_denied, same_exec, logged, missing,
156 				request->audit.u.net->sk,
157 				ntohs(request->audit.u.net->sport),
158 				ntohs(request->audit.u.net->dport));
159 		break;
160 	case LANDLOCK_REQUEST_PTRACE:
161 		if (trace_landlock_deny_ptrace_enabled())
162 			trace_landlock_deny_ptrace(youngest_denied, same_exec,
163 						   logged,
164 						   request->other_domain_id,
165 						   request->audit.u.tsk);
166 		break;
167 	case LANDLOCK_REQUEST_SCOPE_SIGNAL:
168 		if (trace_landlock_deny_scope_signal_enabled())
169 			trace_landlock_deny_scope_signal(
170 				youngest_denied, same_exec, logged,
171 				request->other_domain_id, request->audit.u.tsk);
172 		break;
173 	case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET:
174 		if (trace_landlock_deny_scope_abstract_unix_socket_enabled())
175 			trace_landlock_deny_scope_abstract_unix_socket(
176 				youngest_denied, same_exec, logged,
177 				request->other_domain_id,
178 				request->audit.u.net->sk);
179 		break;
180 	default:
181 		WARN_ONCE(1, "Unhandled Landlock request type %d",
182 			  request->type);
183 		break;
184 	}
185 }
186