xref: /linux/security/landlock/trace.c (revision d41d0021a6ea3e9fcd14126a00fead47f981c46e)
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 <kunit/test.h>
10 #include <linux/cleanup.h>
11 #include <linux/dcache.h>
12 #include <linux/err.h>
13 #include <linux/fs.h>
14 #include <linux/lsm_audit.h>
15 #include <net/sock.h>
16 
17 #include "access.h"
18 #include "domain.h"
19 #include "fs.h"
20 #include "log.h"
21 #include "ruleset.h"
22 #include "trace.h"
23 
24 /*
25  * Generates the tracepoint definitions in this translation unit.  The trace
26  * event header dereferences the traced objects in TP_fast_assign, so the full
27  * struct definitions (e.g. ruleset.h, domain.h) must be included before it.
28  */
29 #define CREATE_TRACE_POINTS
30 #include <trace/events/landlock.h>
31 
32 /**
33  * landlock_trace_free_domain - Emit a tracepoint on domain deallocation
34  *
35  * @hierarchy: The domain's hierarchy being deallocated.
36  *
37  * Fires only for a hierarchy whose creation event was emitted, i.e. one that
38  * left LANDLOCK_LOG_UNCOMMITTED in landlock_restrict_self().  This keeps the
39  * create/free pair balanced: a hierarchy that never became observable is freed
40  * silently, while a domain that landlock_restrict_self() created and a
41  * thread-sync failure then aborted still fires free_domain, because its
42  * creation event already fired.
43  *
44  * Called from landlock_log_free_domain().
45  */
46 void landlock_trace_free_domain(const struct landlock_hierarchy *const hierarchy)
47 {
48 	/*
49 	 * The log_status read is a correctness guard (keep the create/free pair
50 	 * balanced), not a cost guard, so this cold path needs no
51 	 * trace_..._enabled() check: the tracepoint is a static-branch no-op
52 	 * when disabled.  The denial path guards trace_..._enabled() instead
53 	 * because it does expensive __getname()/path work before emitting.
54 	 */
55 	if (READ_ONCE(hierarchy->log_status) != LANDLOCK_LOG_UNCOMMITTED)
56 		trace_landlock_free_domain(hierarchy);
57 }
58 
59 /**
60  * landlock_trace_denial - Emit a tracepoint for a denied access request
61  *
62  * @request: Detail of the user space request.
63  * @youngest_denied: The youngest hierarchy node that denied the access.
64  * @missing: The set of denied access rights.
65  * @same_exec: Whether the current task is the same executable that called
66  *             landlock_restrict_self() for the denying domain, as computed
67  *             by landlock_log_denial().
68  * @logged: Whether the domain's policy selects this denial for logging, as
69  *          computed by landlock_log_denial().
70  *
71  * Emits the tracepoint matching @request->type when its event is enabled.
72  * Unlike audit, fires regardless of @logged; the value is recorded in the event
73  * so consumers can filter on it.
74  *
75  * Called from landlock_log_denial().
76  */
77 void landlock_trace_denial(
78 	const struct landlock_request *const request,
79 	const struct landlock_hierarchy *const youngest_denied,
80 	const access_mask_t missing, const bool same_exec, const bool logged)
81 {
82 	switch (request->type) {
83 	case LANDLOCK_REQUEST_FS_ACCESS:
84 	case LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY:
85 		if (trace_landlock_deny_access_fs_enabled()) {
86 			char *buf __free(__putname) = __getname();
87 			struct path dentry_path;
88 			const char *pathname;
89 			const struct path *path = NULL;
90 
91 			/*
92 			 * Selects the path from the audit data type, as
93 			 * dump_common_audit_data() does.  A FS_ACCESS denial
94 			 * carries a file (hook_file_truncate) or an ioctl op
95 			 * (hook_file_ioctl) rather than a path;
96 			 * FS_CHANGE_TOPOLOGY carries a path or a bare dentry.
97 			 * Reading the wrong union member would dereference
98 			 * garbage, so every reachable type is handled here.
99 			 */
100 			switch (request->audit.type) {
101 			case LSM_AUDIT_DATA_FILE:
102 				path = &request->audit.u.file->f_path;
103 				break;
104 			case LSM_AUDIT_DATA_IOCTL_OP:
105 				path = &request->audit.u.op->path;
106 				break;
107 			case LSM_AUDIT_DATA_DENTRY:
108 				/*
109 				 * Build a path on the stack with the real
110 				 * dentry so TP_fast_assign can extract dev and
111 				 * ino; the mnt field is unused there.
112 				 */
113 				dentry_path = (struct path){
114 					.dentry = request->audit.u.dentry,
115 				};
116 				path = &dentry_path;
117 				break;
118 			case LSM_AUDIT_DATA_PATH:
119 				path = &request->audit.u.path;
120 				break;
121 			default:
122 				WARN_ONCE(1,
123 					  "Unhandled Landlock FS audit type %d",
124 					  request->audit.type);
125 				break;
126 			}
127 
128 			if (!path)
129 				break;
130 
131 			if (!buf) {
132 				pathname = "<no_mem>";
133 			} else if (request->audit.type ==
134 				   LSM_AUDIT_DATA_DENTRY) {
135 				/* No vfsmount: render the dentry path alone. */
136 				pathname = dentry_path_raw(
137 					request->audit.u.dentry, buf, PATH_MAX);
138 				if (IS_ERR(pathname))
139 					pathname =
140 						PTR_ERR(pathname) ==
141 								-ENAMETOOLONG ?
142 							"<too_long>" :
143 							"<unreachable>";
144 			} else {
145 				pathname = resolve_path_for_trace(path, buf);
146 			}
147 
148 			trace_landlock_deny_access_fs(youngest_denied,
149 						      same_exec, logged,
150 						      missing, path, pathname);
151 		}
152 		break;
153 	case LANDLOCK_REQUEST_NET_ACCESS:
154 		if (trace_landlock_deny_access_net_enabled())
155 			trace_landlock_deny_access_net(
156 				youngest_denied, same_exec, logged, missing,
157 				request->audit.u.net->sk,
158 				ntohs(request->audit.u.net->sport),
159 				ntohs(request->audit.u.net->dport));
160 		break;
161 	case LANDLOCK_REQUEST_PTRACE:
162 		if (trace_landlock_deny_ptrace_enabled())
163 			trace_landlock_deny_ptrace(youngest_denied, same_exec,
164 						   logged,
165 						   request->other_domain_id,
166 						   request->audit.u.tsk);
167 		break;
168 	case LANDLOCK_REQUEST_SCOPE_SIGNAL:
169 		if (trace_landlock_deny_scope_signal_enabled())
170 			trace_landlock_deny_scope_signal(
171 				youngest_denied, same_exec, logged,
172 				request->other_domain_id, request->audit.u.tsk);
173 		break;
174 	case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET:
175 		if (trace_landlock_deny_scope_abstract_unix_socket_enabled())
176 			trace_landlock_deny_scope_abstract_unix_socket(
177 				youngest_denied, same_exec, logged,
178 				request->other_domain_id,
179 				request->audit.u.net->sk);
180 		break;
181 	default:
182 		WARN_ONCE(1, "Unhandled Landlock request type %d",
183 			  request->type);
184 		break;
185 	}
186 }
187 
188 #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
189 
190 static void test_trace_seq_init(struct trace_seq *const seq, const size_t size)
191 {
192 	memset(seq, 0, sizeof(*seq));
193 	seq_buf_init(&seq->seq, seq->buffer, size);
194 }
195 
196 static void test_untrusted_str_data(struct kunit *const test)
197 {
198 	const char binary[] = { 'a', '\0', '<' };
199 	static const char ellipsis[] = "\xe2\x80\xa6";
200 	struct trace_seq *const seq =
201 		kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL);
202 	const char *output;
203 
204 	KUNIT_ASSERT_NOT_NULL(test, seq);
205 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
206 	output = __trace_print_untrusted_str(seq, "<too_long>", 10);
207 	KUNIT_ASSERT_NOT_NULL(test, output);
208 	KUNIT_EXPECT_STREQ(test, output, "<too_long>");
209 
210 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
211 	output = __trace_print_untrusted_str(seq, binary, sizeof(binary));
212 	KUNIT_ASSERT_NOT_NULL(test, output);
213 	KUNIT_EXPECT_STREQ(test, output, "a\\000<");
214 
215 	/* Input ellipsis bytes are escaped and cannot mimic the raw marker. */
216 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
217 	output = __trace_print_untrusted_str(seq, ellipsis,
218 					     sizeof(ellipsis) - 1);
219 	KUNIT_ASSERT_NOT_NULL(test, output);
220 	KUNIT_EXPECT_STREQ(test, output, "\\342\\200\\246");
221 }
222 
223 static void test_untrusted_str_boundaries(struct kunit *const test)
224 {
225 	static const char escaped_space[] = "\\040";
226 	const size_t output_size = TRACE_UNTRUSTED_STR_OUTPUT_SIZE;
227 	const size_t marker_len = sizeof(TRACE_TRUNCATION_MARKER) - 1;
228 	const size_t escape_len = sizeof(escaped_space) - 1;
229 	const size_t exact_prefix_len =
230 		output_size - marker_len - 1 - escape_len;
231 	const size_t short_prefix_len = exact_prefix_len + 1;
232 	struct trace_seq *const seq =
233 		kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL);
234 	char *const input = kunit_kmalloc(test, output_size + 1, GFP_KERNEL);
235 	char *const expected = kunit_kmalloc(test, output_size, GFP_KERNEL);
236 	const char *output;
237 
238 	KUNIT_ASSERT_NOT_NULL(test, seq);
239 	KUNIT_ASSERT_NOT_NULL(test, input);
240 	KUNIT_ASSERT_NOT_NULL(test, expected);
241 
242 	/* The escaped string and its trailing NUL exactly fit the limit. */
243 	memset(input, 'a', output_size - 1);
244 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
245 	output = __trace_print_untrusted_str(seq, input, output_size - 1);
246 	KUNIT_ASSERT_NOT_NULL(test, output);
247 	KUNIT_EXPECT_EQ(test, seq->seq.len, output_size);
248 	KUNIT_EXPECT_EQ(test, memcmp(output, input, output_size - 1), 0);
249 
250 	/* Stop before a four-byte escape when only three bytes remain. */
251 	memset(input, 'a', short_prefix_len);
252 	input[short_prefix_len] = ' ';
253 	memset(input + short_prefix_len + 1, 'b', 5);
254 	memset(expected, 'a', short_prefix_len);
255 	memcpy(expected + short_prefix_len, TRACE_TRUNCATION_MARKER,
256 	       marker_len + 1);
257 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
258 	output = __trace_print_untrusted_str(seq, input, short_prefix_len + 6);
259 	KUNIT_ASSERT_NOT_NULL(test, output);
260 	KUNIT_EXPECT_STREQ(test, output, expected);
261 
262 	/* Include a four-byte escape that exactly fills the prefix capacity. */
263 	memset(input, 'a', exact_prefix_len);
264 	input[exact_prefix_len] = ' ';
265 	memset(input + exact_prefix_len + 1, 'b', marker_len + 1);
266 	memset(expected, 'a', exact_prefix_len);
267 	memcpy(expected + exact_prefix_len, escaped_space, escape_len);
268 	memcpy(expected + exact_prefix_len + escape_len,
269 	       TRACE_TRUNCATION_MARKER, marker_len + 1);
270 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
271 	output = __trace_print_untrusted_str(seq, input,
272 					     exact_prefix_len + marker_len + 2);
273 	KUNIT_ASSERT_NOT_NULL(test, output);
274 	KUNIT_EXPECT_STREQ(test, output, expected);
275 
276 	/* Literal backslashes remain escaped in complete output. */
277 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
278 	output = __trace_print_untrusted_str(seq, "/\\000", 5);
279 	KUNIT_ASSERT_NOT_NULL(test, output);
280 	KUNIT_EXPECT_STREQ(test, output, "/\\\\000");
281 }
282 
283 static void test_untrusted_str_cursor(struct kunit *const test)
284 {
285 	const size_t padding_len =
286 		TRACE_SEQ_BUFFER_SIZE - TRACE_UNTRUSTED_STR_OUTPUT_SIZE + 1;
287 	struct trace_seq *const seq =
288 		kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL);
289 	char *const padding = kunit_kzalloc(test, padding_len, GFP_KERNEL);
290 	const char *output;
291 
292 	KUNIT_ASSERT_NOT_NULL(test, seq);
293 	KUNIT_ASSERT_NOT_NULL(test, padding);
294 
295 	/* Accept available space exactly equal to the fixed reservation. */
296 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
297 	trace_seq_putmem(seq, padding, padding_len - 1);
298 	output = __trace_print_untrusted_str(seq, "/a", 2);
299 	KUNIT_ASSERT_NOT_NULL(test, output);
300 	KUNIT_EXPECT_STREQ(test, output, "/a");
301 	KUNIT_EXPECT_EQ(test, seq->seq.len, padding_len - 1 + sizeof("/a"));
302 
303 	/* Reject one byte less without changing the scratch cursor. */
304 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
305 	trace_seq_putmem(seq, padding, padding_len);
306 	output = __trace_print_untrusted_str(seq, "/a", 2);
307 	KUNIT_EXPECT_NULL(test, output);
308 	KUNIT_EXPECT_EQ(test, seq->seq.len, padding_len);
309 }
310 
311 static void test_untrusted_str_composition(struct kunit *const test)
312 {
313 	static const struct trace_print_flags flags[] = {
314 		{ .mask = 1, .name = "read" },
315 	};
316 	const size_t output_size = TRACE_UNTRUSTED_STR_OUTPUT_SIZE;
317 	const size_t prefix_len = output_size - sizeof(TRACE_TRUNCATION_MARKER);
318 	struct trace_seq *const seq =
319 		kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL);
320 	char *const expected = kunit_kmalloc(test, output_size, GFP_KERNEL);
321 	char *const path = kunit_kmalloc(test, output_size, GFP_KERNEL);
322 	const char *flags_output, *path_output;
323 
324 	KUNIT_ASSERT_NOT_NULL(test, seq);
325 	KUNIT_ASSERT_NOT_NULL(test, expected);
326 	KUNIT_ASSERT_NOT_NULL(test, path);
327 	memset(path, 'a', output_size);
328 	memset(expected, 'a', prefix_len);
329 	memcpy(expected + prefix_len, TRACE_TRUNCATION_MARKER,
330 	       sizeof(TRACE_TRUNCATION_MARKER));
331 
332 	/* Exercise both legal TP_printk() sibling evaluation orders. */
333 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
334 	path_output = __trace_print_untrusted_str(seq, path, output_size);
335 	flags_output =
336 		trace_print_flags_seq(seq, "|", 1, flags, ARRAY_SIZE(flags));
337 	KUNIT_ASSERT_NOT_NULL(test, path_output);
338 	KUNIT_EXPECT_STREQ(test, path_output, expected);
339 	KUNIT_EXPECT_STREQ(test, flags_output, "read");
340 
341 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
342 	flags_output =
343 		trace_print_flags_seq(seq, "|", 1, flags, ARRAY_SIZE(flags));
344 	path_output = __trace_print_untrusted_str(seq, path, output_size);
345 	KUNIT_ASSERT_NOT_NULL(test, path_output);
346 	KUNIT_EXPECT_STREQ(test, path_output, expected);
347 	KUNIT_EXPECT_STREQ(test, flags_output, "read");
348 }
349 
350 static struct kunit_case test_cases[] = {
351 	/* clang-format off */
352 	KUNIT_CASE(test_untrusted_str_data),
353 	KUNIT_CASE(test_untrusted_str_boundaries),
354 	KUNIT_CASE(test_untrusted_str_cursor),
355 	KUNIT_CASE(test_untrusted_str_composition),
356 	{}
357 	/* clang-format on */
358 };
359 
360 static struct kunit_suite test_suite = {
361 	.name = "landlock_trace",
362 	.test_cases = test_cases,
363 };
364 
365 kunit_test_suite(test_suite);
366 
367 #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
368