xref: /linux/tools/testing/selftests/landlock/trace_test.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Landlock tests - Tracepoints
4  *
5  * Copyright © 2026 Cloudflare, Inc.
6  */
7 
8 #define _GNU_SOURCE
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <linux/landlock.h>
12 #include <pthread.h>
13 #include <sched.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <sys/mount.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21 
22 #include "common.h"
23 #include "trace.h"
24 
25 #define TRACE_TASK "trace_test"
26 
27 /* clang-format off */
28 FIXTURE(trace) {
29 	/* clang-format on */
30 	int tracefs_ok;
31 };
32 
33 FIXTURE_SETUP(trace)
34 {
35 	int ret;
36 
37 	set_cap(_metadata, CAP_SYS_ADMIN);
38 	ASSERT_EQ(0, unshare(CLONE_NEWNS));
39 	ASSERT_EQ(0, mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL));
40 
41 	ret = tracefs_fixture_setup();
42 	if (ret) {
43 		clear_cap(_metadata, CAP_SYS_ADMIN);
44 		self->tracefs_ok = 0;
45 		SKIP(return, "tracefs not available");
46 	}
47 	self->tracefs_ok = 1;
48 
49 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_CREATE_RULESET_ENABLE, true));
50 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_CREATE_DOMAIN_ENABLE, true));
51 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_ENFORCE_DOMAIN_ENABLE, true));
52 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_ADD_RULE_FS_ENABLE, true));
53 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_ADD_RULE_NET_ENABLE, true));
54 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_CHECK_RULE_FS_ENABLE, true));
55 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_CHECK_RULE_NET_ENABLE, true));
56 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_DENY_ACCESS_FS_ENABLE, true));
57 	ASSERT_EQ(0,
58 		  tracefs_enable_event(TRACEFS_DENY_ACCESS_NET_ENABLE, true));
59 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_FREE_DOMAIN_ENABLE, true));
60 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_FREE_RULESET_ENABLE, true));
61 	ASSERT_EQ(0, tracefs_clear());
62 	clear_cap(_metadata, CAP_SYS_ADMIN);
63 }
64 
65 FIXTURE_TEARDOWN(trace)
66 {
67 	if (!self->tracefs_ok)
68 		return;
69 
70 	/* Disables landlock events and clears PID filter. */
71 	set_cap(_metadata, CAP_SYS_ADMIN);
72 	tracefs_enable_event(TRACEFS_CREATE_RULESET_ENABLE, false);
73 	tracefs_enable_event(TRACEFS_CREATE_DOMAIN_ENABLE, false);
74 	tracefs_enable_event(TRACEFS_ENFORCE_DOMAIN_ENABLE, false);
75 	tracefs_enable_event(TRACEFS_ADD_RULE_FS_ENABLE, false);
76 	tracefs_enable_event(TRACEFS_ADD_RULE_NET_ENABLE, false);
77 	tracefs_enable_event(TRACEFS_CHECK_RULE_FS_ENABLE, false);
78 	tracefs_enable_event(TRACEFS_CHECK_RULE_NET_ENABLE, false);
79 	tracefs_enable_event(TRACEFS_DENY_ACCESS_FS_ENABLE, false);
80 	tracefs_enable_event(TRACEFS_DENY_ACCESS_NET_ENABLE, false);
81 	tracefs_enable_event(TRACEFS_FREE_DOMAIN_ENABLE, false);
82 	tracefs_enable_event(TRACEFS_FREE_RULESET_ENABLE, false);
83 	tracefs_clear_pid_filter();
84 	clear_cap(_metadata, CAP_SYS_ADMIN);
85 
86 	/*
87 	 * The mount namespace is cleaned up automatically when the test process
88 	 * (harness child) exits.
89 	 */
90 }
91 
92 /*
93  * Verifies that no trace events are emitted when the tracepoints are disabled.
94  */
95 TEST_F(trace, no_trace_when_disabled)
96 {
97 	char *buf;
98 
99 	/* Disable all landlock events. */
100 	set_cap(_metadata, CAP_SYS_ADMIN);
101 	ASSERT_EQ(0,
102 		  tracefs_enable_event(TRACEFS_CREATE_RULESET_ENABLE, false));
103 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_CREATE_DOMAIN_ENABLE, false));
104 	ASSERT_EQ(0,
105 		  tracefs_enable_event(TRACEFS_ENFORCE_DOMAIN_ENABLE, false));
106 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_ADD_RULE_FS_ENABLE, false));
107 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_ADD_RULE_NET_ENABLE, false));
108 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_CHECK_RULE_FS_ENABLE, false));
109 	ASSERT_EQ(0,
110 		  tracefs_enable_event(TRACEFS_CHECK_RULE_NET_ENABLE, false));
111 	ASSERT_EQ(0,
112 		  tracefs_enable_event(TRACEFS_DENY_ACCESS_FS_ENABLE, false));
113 	ASSERT_EQ(0,
114 		  tracefs_enable_event(TRACEFS_DENY_ACCESS_NET_ENABLE, false));
115 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_DENY_PTRACE_ENABLE, false));
116 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_DENY_SCOPE_SIGNAL_ENABLE,
117 					  false));
118 	ASSERT_EQ(0, tracefs_enable_event(
119 			     TRACEFS_DENY_SCOPE_ABSTRACT_UNIX_SOCKET_ENABLE,
120 			     false));
121 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_FREE_DOMAIN_ENABLE, false));
122 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_FREE_RULESET_ENABLE, false));
123 	ASSERT_EQ(0, tracefs_clear());
124 	clear_cap(_metadata, CAP_SYS_ADMIN);
125 
126 	/*
127 	 * Trigger both allowed and denied accesses to verify neither check_rule
128 	 * nor check_access events fire when disabled.
129 	 */
130 	sandbox_child_fs_access(_metadata, "/usr", LANDLOCK_ACCESS_FS_READ_DIR,
131 				LANDLOCK_ACCESS_FS_READ_DIR, "/tmp");
132 
133 	/* Read trace buffer and verify no landlock events at all. */
134 	buf = tracefs_read_buf();
135 	ASSERT_NE(NULL, buf);
136 
137 	EXPECT_EQ(0, tracefs_count_matches(buf, "landlock_"))
138 	{
139 		TH_LOG("Expected 0 landlock events when disabled\n%s", buf);
140 	}
141 
142 	free(buf);
143 }
144 
145 /*
146  * Verifies that landlock_create_ruleset emits a trace event with the correct
147  * handled access masks.
148  */
149 TEST_F(trace, create_ruleset)
150 {
151 	struct landlock_ruleset_attr ruleset_attr = {
152 		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
153 		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP,
154 	};
155 	int ruleset_fd;
156 	char *buf, *dot;
157 	char field[64];
158 
159 	ruleset_fd =
160 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
161 	ASSERT_LE(0, ruleset_fd);
162 	ASSERT_EQ(0, close(ruleset_fd));
163 
164 	buf = tracefs_read_buf();
165 	ASSERT_NE(NULL, buf);
166 
167 	EXPECT_EQ(1,
168 		  tracefs_count_matches(buf, REGEX_CREATE_RULESET(TRACE_TASK)))
169 	{
170 		TH_LOG("Expected 1 create_ruleset event\n%s", buf);
171 	}
172 
173 	/* Verify handled_fs matches what we requested. */
174 	EXPECT_EQ(0,
175 		  tracefs_extract_field(buf, REGEX_CREATE_RULESET(TRACE_TASK),
176 					"handled_fs", field, sizeof(field)));
177 	EXPECT_STREQ("read_file", field);
178 
179 	/* Verify handled_net matches. */
180 	EXPECT_EQ(0,
181 		  tracefs_extract_field(buf, REGEX_CREATE_RULESET(TRACE_TASK),
182 					"handled_net", field, sizeof(field)));
183 	EXPECT_STREQ("bind_tcp", field);
184 
185 	/* Verify version is 0 at creation (no rules added yet). */
186 	EXPECT_EQ(0,
187 		  tracefs_extract_field(buf, REGEX_CREATE_RULESET(TRACE_TASK),
188 					"ruleset", field, sizeof(field)));
189 	/* Format is <hex>.<dec>; version is after the dot. */
190 	dot = strchr(field, '.');
191 	ASSERT_NE(0, !!dot);
192 	EXPECT_STREQ("0", dot + 1);
193 
194 	free(buf);
195 }
196 
197 /*
198  * Verifies that the ruleset version increments with each add_rule call and that
199  * create_domain records the correct version.
200  */
201 TEST_F(trace, ruleset_version)
202 {
203 	pid_t pid;
204 	int status;
205 	char *buf;
206 	const char *dot;
207 	char field[64];
208 
209 	ASSERT_EQ(0, tracefs_clear_buf());
210 
211 	pid = fork();
212 	ASSERT_LE(0, pid);
213 
214 	if (pid == 0) {
215 		struct landlock_ruleset_attr ruleset_attr = {
216 			.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
217 		};
218 		struct landlock_path_beneath_attr path_beneath = {
219 			.allowed_access = LANDLOCK_ACCESS_FS_READ_DIR,
220 		};
221 		int ruleset_fd;
222 
223 		ruleset_fd = landlock_create_ruleset(&ruleset_attr,
224 						     sizeof(ruleset_attr), 0);
225 		if (ruleset_fd < 0)
226 			_exit(1);
227 
228 		/* First rule: version becomes 1. */
229 		path_beneath.parent_fd =
230 			open("/usr", O_PATH | O_DIRECTORY | O_CLOEXEC);
231 		if (path_beneath.parent_fd < 0)
232 			_exit(1);
233 		landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
234 				  &path_beneath, 0);
235 		close(path_beneath.parent_fd);
236 
237 		/* Second rule: version becomes 2. */
238 		path_beneath.parent_fd =
239 			open("/tmp", O_PATH | O_DIRECTORY | O_CLOEXEC);
240 		if (path_beneath.parent_fd < 0)
241 			_exit(1);
242 		landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
243 				  &path_beneath, 0);
244 		close(path_beneath.parent_fd);
245 
246 		prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
247 		if (landlock_restrict_self(ruleset_fd, 0))
248 			_exit(1);
249 		close(ruleset_fd);
250 		_exit(0);
251 	}
252 
253 	ASSERT_EQ(pid, waitpid(pid, &status, 0));
254 	ASSERT_TRUE(WIFEXITED(status));
255 	EXPECT_EQ(0, WEXITSTATUS(status));
256 
257 	buf = tracefs_read_buf();
258 	ASSERT_NE(NULL, buf);
259 
260 	/* Verify create_ruleset has version=0. */
261 	ASSERT_EQ(0,
262 		  tracefs_extract_field(buf, REGEX_CREATE_RULESET(TRACE_TASK),
263 					"ruleset", field, sizeof(field)));
264 	dot = strchr(field, '.');
265 	ASSERT_NE(0, !!dot);
266 	EXPECT_STREQ("0", dot + 1);
267 
268 	/* Verify 2 add_rule_fs events were emitted. */
269 	EXPECT_EQ(2, tracefs_count_matches(buf, REGEX_ADD_RULE_FS(TRACE_TASK)))
270 	{
271 		TH_LOG("Expected 2 add_rule_fs events\n%s", buf);
272 	}
273 
274 	/*
275 	 * Verify create_domain records version=2 (after 2 add_rule calls).  The
276 	 * ruleset field format is <hex_id>.<dec_version>.
277 	 */
278 	ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CREATE_DOMAIN(TRACE_TASK),
279 					   "ruleset", field, sizeof(field)));
280 	dot = strchr(field, '.');
281 	ASSERT_NE(0, !!dot);
282 	EXPECT_STREQ("2", dot + 1);
283 
284 	free(buf);
285 }
286 
287 /*
288  * Verifies that landlock_create_domain emits a trace event linking the ruleset
289  * ID to the new domain ID.
290  */
291 TEST_F(trace, create_domain)
292 {
293 	pid_t pid;
294 	int status, check_count;
295 	char *buf;
296 	char parent_id[64], domain_id[64], check_domain[64];
297 
298 	/* Clear before the sandboxed child. */
299 	ASSERT_EQ(0, tracefs_clear_buf());
300 
301 	pid = fork();
302 	ASSERT_LE(0, pid);
303 
304 	if (pid == 0) {
305 		struct landlock_ruleset_attr ruleset_attr = {
306 			.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
307 		};
308 		struct landlock_path_beneath_attr path_beneath = {
309 			.allowed_access = LANDLOCK_ACCESS_FS_READ_DIR,
310 		};
311 		int ruleset_fd, fd;
312 
313 		ruleset_fd = landlock_create_ruleset(&ruleset_attr,
314 						     sizeof(ruleset_attr), 0);
315 		if (ruleset_fd < 0)
316 			_exit(1);
317 
318 		path_beneath.parent_fd =
319 			open("/usr", O_PATH | O_DIRECTORY | O_CLOEXEC);
320 		if (path_beneath.parent_fd < 0)
321 			_exit(1);
322 
323 		landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
324 				  &path_beneath, 0);
325 		close(path_beneath.parent_fd);
326 
327 		prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
328 		if (landlock_restrict_self(ruleset_fd, 0))
329 			_exit(1);
330 		close(ruleset_fd);
331 
332 		/* Trigger a check_rule to verify domain_id correlation. */
333 		fd = open("/usr", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
334 		if (fd >= 0)
335 			close(fd);
336 
337 		_exit(0);
338 	}
339 
340 	ASSERT_EQ(pid, waitpid(pid, &status, 0));
341 	ASSERT_TRUE(WIFEXITED(status));
342 	EXPECT_EQ(0, WEXITSTATUS(status));
343 
344 	buf = tracefs_read_buf();
345 	ASSERT_NE(NULL, buf);
346 
347 	/* Verify create_domain event exists. */
348 	EXPECT_EQ(1,
349 		  tracefs_count_matches(buf, REGEX_CREATE_DOMAIN(TRACE_TASK)))
350 	{
351 		TH_LOG("Expected 1 create_domain event\n%s", buf);
352 	}
353 
354 	/* Extract the domain ID from create_domain. */
355 	EXPECT_EQ(0, tracefs_extract_field(buf, REGEX_CREATE_DOMAIN(TRACE_TASK),
356 					   "domain", domain_id,
357 					   sizeof(domain_id)));
358 
359 	/* Verify domain ID is non-zero. */
360 	EXPECT_NE(0, strcmp(domain_id, "0"));
361 
362 	/* Verify parent=0 (first restriction, no prior domain). */
363 	EXPECT_EQ(0, tracefs_extract_field(buf, REGEX_CREATE_DOMAIN(TRACE_TASK),
364 					   "parent", parent_id,
365 					   sizeof(parent_id)));
366 	EXPECT_STREQ("0", parent_id);
367 
368 	/*
369 	 * Verify the same domain ID appears in the check_rule event, confirming
370 	 * end-to-end correlation.
371 	 */
372 	check_count =
373 		tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK));
374 	ASSERT_LE(1, check_count)
375 	{
376 		TH_LOG("Expected check_rule_fs events\n%s", buf);
377 	}
378 
379 	EXPECT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK),
380 					   "domain", check_domain,
381 					   sizeof(check_domain)));
382 	EXPECT_STREQ(domain_id, check_domain);
383 
384 	free(buf);
385 }
386 
387 /* Builds a rule-less scope-based ruleset; returns the fd or -1. */
388 static int build_enforce_ruleset(void)
389 {
390 	const struct landlock_ruleset_attr attr = {
391 		.scoped = LANDLOCK_SCOPE_SIGNAL,
392 	};
393 
394 	return landlock_create_ruleset(&attr, sizeof(attr), 0);
395 }
396 
397 /*
398  * Verifies that nested landlock_restrict_self calls produce trace events with
399  * correct parent domain IDs: the second create_domain's parent should be the
400  * first domain's ID.
401  */
402 TEST_F(trace, create_domain_nested)
403 {
404 	pid_t pid;
405 	int status;
406 	char *buf;
407 	const char *after_first;
408 	char first_domain[64], first_parent[64], second_parent[64];
409 
410 	ASSERT_EQ(0, tracefs_clear_buf());
411 
412 	pid = fork();
413 	ASSERT_LE(0, pid);
414 
415 	if (pid == 0) {
416 		int ruleset_fd;
417 
418 		/* First restriction. */
419 		ruleset_fd = build_enforce_ruleset();
420 		if (ruleset_fd < 0)
421 			_exit(1);
422 		prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
423 		if (landlock_restrict_self(ruleset_fd, 0))
424 			_exit(1);
425 		close(ruleset_fd);
426 
427 		/* Second restriction (nested). */
428 		ruleset_fd = build_enforce_ruleset();
429 		if (ruleset_fd < 0)
430 			_exit(1);
431 		if (landlock_restrict_self(ruleset_fd, 0))
432 			_exit(1);
433 		close(ruleset_fd);
434 
435 		_exit(0);
436 	}
437 
438 	ASSERT_EQ(pid, waitpid(pid, &status, 0));
439 	ASSERT_TRUE(WIFEXITED(status));
440 	EXPECT_EQ(0, WEXITSTATUS(status));
441 
442 	buf = tracefs_read_buf();
443 	ASSERT_NE(NULL, buf);
444 
445 	/* Should have 2 create_domain events. */
446 	EXPECT_EQ(2,
447 		  tracefs_count_matches(buf, REGEX_CREATE_DOMAIN(TRACE_TASK)))
448 	{
449 		TH_LOG("Expected 2 create_domain events\n%s", buf);
450 	}
451 
452 	/*
453 	 * Extract domain and parent from each create_domain event.  The first
454 	 * event (parent=0) is the outer domain; the second (parent!=0) is the
455 	 * nested domain whose parent should match the first domain's ID.
456 	 */
457 	ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CREATE_DOMAIN(TRACE_TASK),
458 					   "domain", first_domain,
459 					   sizeof(first_domain)));
460 	ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CREATE_DOMAIN(TRACE_TASK),
461 					   "parent", first_parent,
462 					   sizeof(first_parent)));
463 	EXPECT_STREQ("0", first_parent);
464 
465 	/*
466 	 * Find the second create_domain by scanning past the first.
467 	 * tracefs_extract_field returns the first match, so search in the
468 	 * buffer after the first event.
469 	 *
470 	 * Skip past the first create_domain line. tracefs_extract_field matches
471 	 * the first line that matches the regex, so passing the buffer after
472 	 * the first matching line gives us the second event.
473 	 */
474 	after_first = strstr(buf, "landlock_create_domain:");
475 	ASSERT_NE(NULL, after_first);
476 	after_first = strchr(after_first, '\n');
477 	ASSERT_NE(NULL, after_first);
478 
479 	ASSERT_EQ(0, tracefs_extract_field(
480 			     after_first + 1, REGEX_CREATE_DOMAIN(TRACE_TASK),
481 			     "parent", second_parent, sizeof(second_parent)));
482 
483 	/* The second domain's parent should be the first domain's ID. */
484 	EXPECT_STREQ(first_domain, second_parent);
485 
486 	free(buf);
487 }
488 
489 /*
490  * Verifies that landlock_add_rule does not emit a trace event when the syscall
491  * fails (e.g., invalid ruleset fd).
492  */
493 TEST_F(trace, add_rule_invalid_fd)
494 {
495 	struct landlock_path_beneath_attr path_beneath = {
496 		.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
497 	};
498 	char *buf;
499 
500 	path_beneath.parent_fd = open("/usr", O_PATH | O_DIRECTORY | O_CLOEXEC);
501 	ASSERT_LE(0, path_beneath.parent_fd);
502 
503 	/* Invalid ruleset fd (-1). */
504 	ASSERT_EQ(-1, landlock_add_rule(-1, LANDLOCK_RULE_PATH_BENEATH,
505 					&path_beneath, 0));
506 	ASSERT_EQ(0, close(path_beneath.parent_fd));
507 
508 	buf = tracefs_read_buf();
509 	ASSERT_NE(NULL, buf);
510 
511 	EXPECT_EQ(0, tracefs_count_matches(buf, REGEX_ADD_RULE_FS(TRACE_TASK)))
512 	{
513 		TH_LOG("No add_rule_fs event expected on invalid fd\n%s", buf);
514 	}
515 
516 	free(buf);
517 }
518 
519 /*
520  * Verifies that landlock_create_domain does not emit a trace event when the
521  * syscall fails (e.g., invalid ruleset fd or unknown flags).
522  */
523 TEST_F(trace, create_domain_invalid)
524 {
525 	int ruleset_fd;
526 	char *buf;
527 
528 	ruleset_fd = build_enforce_ruleset();
529 	ASSERT_LE(0, ruleset_fd);
530 
531 	/* Clear the trace buffer after create_ruleset event. */
532 	ASSERT_EQ(0, tracefs_clear_buf());
533 
534 	/* Invalid fd. */
535 	ASSERT_EQ(-1, landlock_restrict_self(-1, 0));
536 
537 	/* Unknown flags. */
538 	ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, -1));
539 
540 	ASSERT_EQ(0, close(ruleset_fd));
541 
542 	buf = tracefs_read_buf();
543 	ASSERT_NE(NULL, buf);
544 
545 	EXPECT_EQ(0,
546 		  tracefs_count_matches(buf, REGEX_CREATE_DOMAIN(TRACE_TASK)))
547 	{
548 		TH_LOG("No create_domain event expected on error\n%s", buf);
549 	}
550 
551 	free(buf);
552 }
553 
554 /*
555  * Verifies that trace_landlock_free_domain fires when a domain is deallocated,
556  * with the correct denials count.
557  */
558 TEST_F(trace, free_domain)
559 {
560 	char *buf;
561 	int count;
562 	char denials_field[32];
563 
564 	ASSERT_EQ(0, tracefs_clear_buf());
565 
566 	/*
567 	 * The domain is freed via a work queue (kworker), so the free_domain
568 	 * trace event is emitted from a different PID.  Clear the PID filter
569 	 * BEFORE the child exits, so the kworker event passes the filter when
570 	 * it fires.
571 	 */
572 	set_cap(_metadata, CAP_SYS_ADMIN);
573 	tracefs_clear_pid_filter();
574 	clear_cap(_metadata, CAP_SYS_ADMIN);
575 
576 	sandbox_child_fs_access(_metadata, "/usr", LANDLOCK_ACCESS_FS_READ_DIR,
577 				LANDLOCK_ACCESS_FS_READ_DIR, "/tmp");
578 
579 	/*
580 	 * Wait for the deferred deallocation work to run.  The domain is freed
581 	 * asynchronously from a kworker; poll until the event appears or a
582 	 * timeout is reached.
583 	 */
584 	for (int retry = 0; retry < 10; retry++) {
585 		usleep(100000);
586 
587 		set_cap(_metadata, CAP_SYS_ADMIN);
588 		buf = tracefs_read_trace();
589 		clear_cap(_metadata, CAP_SYS_ADMIN);
590 		ASSERT_NE(NULL, buf);
591 
592 		count = tracefs_count_matches(buf,
593 					      REGEX_FREE_DOMAIN(KWORKER_TASK));
594 		if (count >= 1)
595 			break;
596 		free(buf);
597 		buf = NULL;
598 	}
599 
600 	set_cap(_metadata, CAP_SYS_ADMIN);
601 	ASSERT_EQ(0, tracefs_set_pid_filter(getpid()));
602 	clear_cap(_metadata, CAP_SYS_ADMIN);
603 
604 	ASSERT_NE(NULL, buf);
605 	EXPECT_LE(1, count)
606 	{
607 		TH_LOG("Expected free_domain event, got %d\n%s", count, buf);
608 	}
609 
610 	/* Verify denials count matches the single denial we triggered. */
611 	EXPECT_EQ(0, tracefs_extract_field(buf, REGEX_FREE_DOMAIN(KWORKER_TASK),
612 					   "denials", denials_field,
613 					   sizeof(denials_field)));
614 	EXPECT_STREQ("1", denials_field);
615 
616 	free(buf);
617 }
618 
619 /*
620  * Verifies that deny_access_fs includes the enriched fields: same_exec and
621  * logged.
622  */
623 TEST_F(trace, deny_access_fs_fields)
624 {
625 	char *buf;
626 	char field_buf[64];
627 
628 	ASSERT_EQ(0, tracefs_clear_buf());
629 
630 	/* Trigger a denial: rule for /usr, access /tmp. */
631 	sandbox_child_fs_access(_metadata, "/usr", LANDLOCK_ACCESS_FS_READ_DIR,
632 				LANDLOCK_ACCESS_FS_READ_DIR, "/tmp");
633 
634 	buf = tracefs_read_buf();
635 	ASSERT_NE(NULL, buf);
636 
637 	/* Verify the enriched fields are present and have valid values. */
638 	ASSERT_EQ(0, tracefs_extract_field(
639 			     buf, REGEX_DENY_ACCESS_FS(TRACE_TASK), "same_exec",
640 			     field_buf, sizeof(field_buf)));
641 	/* Child is the same exec that restricted itself. */
642 	EXPECT_STREQ("1", field_buf);
643 
644 	/* Same exec with default flags: audit would log this denial. */
645 	ASSERT_EQ(0, tracefs_extract_field(
646 			     buf, REGEX_DENY_ACCESS_FS(TRACE_TASK), "logged",
647 			     field_buf, sizeof(field_buf)));
648 	EXPECT_STREQ("1", field_buf);
649 
650 	free(buf);
651 }
652 
653 /*
654  * Verifies that same_exec is 1 (true) for denials from the same executable that
655  * called landlock_restrict_self().
656  */
657 TEST_F(trace, same_exec_before_exec)
658 {
659 	pid_t pid;
660 	int status;
661 	char *buf;
662 	char field[64];
663 
664 	ASSERT_EQ(0, tracefs_clear_buf());
665 
666 	pid = fork();
667 	ASSERT_LE(0, pid);
668 
669 	if (pid == 0) {
670 		struct landlock_ruleset_attr attr = {
671 			.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
672 		};
673 		int ruleset_fd, dir_fd;
674 
675 		ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
676 		if (ruleset_fd < 0)
677 			_exit(1);
678 
679 		/* No rules: all read_dir access is denied. */
680 		prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
681 		if (landlock_restrict_self(ruleset_fd, 0))
682 			_exit(1);
683 		close(ruleset_fd);
684 
685 		/* Trigger denial without exec (same executable). */
686 		dir_fd = open(".", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
687 		if (dir_fd >= 0)
688 			close(dir_fd);
689 		_exit(0);
690 	}
691 
692 	ASSERT_EQ(pid, waitpid(pid, &status, 0));
693 	ASSERT_TRUE(WIFEXITED(status));
694 	EXPECT_EQ(0, WEXITSTATUS(status));
695 
696 	buf = tracefs_read_buf();
697 	ASSERT_NE(NULL, buf);
698 
699 	/* Should have at least one deny_access_fs denial. */
700 	EXPECT_LE(1,
701 		  tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK)));
702 
703 	/* Verify same_exec=1 (same executable, no exec). */
704 	ASSERT_EQ(0,
705 		  tracefs_extract_field(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK),
706 					"same_exec", field, sizeof(field)));
707 	EXPECT_STREQ("1", field);
708 
709 	/* Same exec with default flags: audit would log this denial. */
710 	ASSERT_EQ(0,
711 		  tracefs_extract_field(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK),
712 					"logged", field, sizeof(field)));
713 	EXPECT_STREQ("1", field);
714 
715 	free(buf);
716 }
717 
718 /*
719  * Verifies that same_exec is 0 (false) for denials from a process that has
720  * exec'd a new binary after landlock_restrict_self().  The sandboxed child
721  * exec's true which opens "." and triggers a read_dir denial.  Covers the
722  * "trace-only" visibility condition: with same_exec=0 and the default
723  * log_new_exec=0, audit suppresses the denial (logged=0) but the trace event
724  * still fires.
725  */
726 TEST_F(trace, same_exec_after_exec)
727 {
728 	char *buf;
729 	char field[64];
730 
731 	ASSERT_EQ(0, tracefs_clear_buf());
732 
733 	sandbox_child_exec_true(_metadata, 0);
734 
735 	buf = tracefs_read_buf();
736 	ASSERT_NE(NULL, buf);
737 
738 	EXPECT_LE(1, tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS("true")));
739 
740 	/* Verify same_exec=0 (different executable after exec). */
741 	ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_DENY_ACCESS_FS("true"),
742 					   "same_exec", field, sizeof(field)));
743 	EXPECT_STREQ("0", field);
744 
745 	/*
746 	 * same_exec=0 with default log_new_exec=0: audit suppresses (logged=0).
747 	 */
748 	ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_DENY_ACCESS_FS("true"),
749 					   "logged", field, sizeof(field)));
750 	EXPECT_STREQ("0", field);
751 
752 	free(buf);
753 }
754 
755 /*
756  * Verifies that LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF suppresses logging
757  * (logged=0) for a denial from the same executable.
758  */
759 TEST_F(trace, log_flags_same_exec_off)
760 {
761 	pid_t pid;
762 	int status;
763 	char *buf;
764 	char field[64];
765 
766 	ASSERT_EQ(0, tracefs_clear_buf());
767 
768 	pid = fork();
769 	ASSERT_LE(0, pid);
770 
771 	if (pid == 0) {
772 		struct landlock_ruleset_attr attr = {
773 			.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
774 		};
775 		int ruleset_fd, dir_fd;
776 
777 		ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
778 		if (ruleset_fd < 0)
779 			_exit(1);
780 
781 		prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
782 		if (landlock_restrict_self(
783 			    ruleset_fd,
784 			    LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF))
785 			_exit(1);
786 		close(ruleset_fd);
787 
788 		dir_fd = open(".", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
789 		if (dir_fd >= 0)
790 			close(dir_fd);
791 		_exit(0);
792 	}
793 
794 	ASSERT_EQ(pid, waitpid(pid, &status, 0));
795 	ASSERT_TRUE(WIFEXITED(status));
796 	EXPECT_EQ(0, WEXITSTATUS(status));
797 
798 	buf = tracefs_read_buf();
799 	ASSERT_NE(NULL, buf);
800 
801 	EXPECT_LE(1,
802 		  tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK)));
803 
804 	/* Same-exec denial with LOG_SAME_EXEC_OFF: audit suppresses it. */
805 	ASSERT_EQ(0,
806 		  tracefs_extract_field(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK),
807 					"logged", field, sizeof(field)));
808 	EXPECT_STREQ("0", field);
809 
810 	free(buf);
811 }
812 
813 /*
814  * Verifies that LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON causes a post-exec
815  * denial to be logged (logged=1).  The child exec's true so that the denial
816  * comes from a new executable (same_exec=0).
817  */
818 TEST_F(trace, log_flags_new_exec_on)
819 {
820 	char *buf;
821 	char field[64];
822 
823 	ASSERT_EQ(0, tracefs_clear_buf());
824 
825 	sandbox_child_exec_true(_metadata,
826 				LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON);
827 
828 	buf = tracefs_read_buf();
829 	ASSERT_NE(NULL, buf);
830 
831 	EXPECT_LE(1, tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS("true")));
832 
833 	ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_DENY_ACCESS_FS("true"),
834 					   "same_exec", field, sizeof(field)));
835 	EXPECT_STREQ("0", field);
836 
837 	/* LOG_NEW_EXEC_ON: the post-exec denial (same_exec=0) is logged. */
838 	ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_DENY_ACCESS_FS("true"),
839 					   "logged", field, sizeof(field)));
840 	EXPECT_STREQ("1", field);
841 
842 	free(buf);
843 }
844 
845 /*
846  * Verifies that denials suppressed by audit log flags are still counted in
847  * num_denials.  The child restricts itself with default flags (log_same_exec=1,
848  * log_new_exec=0), then execs true which attempts to read a denied directory.
849  * After exec, same_exec=0 and log_new_exec=0, so audit suppresses the denial.
850  * But the trace event fires unconditionally and free_domain must report the
851  * correct denials count.
852  */
853 TEST_F(trace, non_audit_visible_denial_counting)
854 {
855 	char *buf = NULL;
856 	char denials_field[32];
857 	int count;
858 
859 	set_cap(_metadata, CAP_SYS_ADMIN);
860 	ASSERT_EQ(0, tracefs_clear());
861 	tracefs_clear_pid_filter();
862 	clear_cap(_metadata, CAP_SYS_ADMIN);
863 
864 	sandbox_child_exec_true(_metadata, 0);
865 
866 	/* Wait for free_domain event with retry. */
867 	for (int retry = 0; retry < 10; retry++) {
868 		usleep(100000);
869 
870 		set_cap(_metadata, CAP_SYS_ADMIN);
871 		buf = tracefs_read_trace();
872 		clear_cap(_metadata, CAP_SYS_ADMIN);
873 		if (!buf)
874 			break;
875 
876 		count = tracefs_count_matches(buf,
877 					      REGEX_FREE_DOMAIN(KWORKER_TASK));
878 		if (count >= 1)
879 			break;
880 		free(buf);
881 		buf = NULL;
882 	}
883 
884 	set_cap(_metadata, CAP_SYS_ADMIN);
885 	ASSERT_EQ(0, tracefs_set_pid_filter(getpid()));
886 	clear_cap(_metadata, CAP_SYS_ADMIN);
887 
888 	/*
889 	 * The denial happened after exec (same_exec=0), so audit would suppress
890 	 * it.  But num_denials counts all denials regardless.
891 	 */
892 	ASSERT_NE(NULL, buf)
893 	{
894 		TH_LOG("free_domain event not found after 10 retries");
895 	}
896 	EXPECT_EQ(0, tracefs_extract_field(buf, REGEX_FREE_DOMAIN(KWORKER_TASK),
897 					   "denials", denials_field,
898 					   sizeof(denials_field)));
899 	EXPECT_STREQ("1", denials_field);
900 
901 	free(buf);
902 }
903 
904 /*
905  * Verifies that landlock_add_rule_net emits a trace event with the correct port
906  * and allowed access mask fields.
907  */
908 TEST_F(trace, add_rule_net_fields)
909 {
910 	struct landlock_ruleset_attr ruleset_attr = {
911 		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP,
912 	};
913 	struct landlock_net_port_attr net_port = {
914 		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
915 		.port = 8080,
916 	};
917 	int ruleset_fd;
918 	char *buf;
919 	char field[64];
920 
921 	ruleset_fd =
922 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
923 	ASSERT_LE(0, ruleset_fd);
924 
925 	ASSERT_EQ(0, tracefs_clear_buf());
926 
927 	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
928 				       &net_port, 0));
929 	close(ruleset_fd);
930 
931 	buf = tracefs_read_buf();
932 	ASSERT_NE(NULL, buf);
933 
934 	EXPECT_EQ(1, tracefs_count_matches(buf, REGEX_ADD_RULE_NET(TRACE_TASK)))
935 	{
936 		TH_LOG("Expected 1 add_rule_net event\n%s", buf);
937 	}
938 
939 	/*
940 	 * Verify the port is in host endianness, matching the UAPI convention
941 	 * (landlock_net_port_attr.port).  On little-endian, htons(8080) is
942 	 * 36895, so this comparison catches byte-order bugs.
943 	 */
944 	EXPECT_EQ(0, tracefs_extract_field(buf, REGEX_ADD_RULE_NET(TRACE_TASK),
945 					   "port", field, sizeof(field)));
946 	EXPECT_STREQ("8080", field);
947 	/*
948 	 * The allowed mask is the absolute value after transformation: the
949 	 * user-requested BIND_TCP plus all unhandled access rights (the other
950 	 * net access bits are unhandled because the ruleset only handles
951 	 * BIND_TCP).
952 	 */
953 	EXPECT_EQ(0,
954 		  tracefs_extract_field(buf, REGEX_ADD_RULE_NET(TRACE_TASK),
955 					"access_rights", field, sizeof(field)));
956 	EXPECT_STREQ("bind_tcp|connect_tcp|bind_udp|connect_send_udp", field);
957 
958 	free(buf);
959 }
960 
961 /*
962  * Verifies that LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF suppresses audit
963  * logging for child domains (logged=0) even though the child's own
964  * per-execution flags are the defaults, while the trace event still fires
965  * (tracing is unconditional).  The parent creates a domain with
966  * LOG_SUBDOMAINS_OFF, then the child creates a sub-domain and triggers a
967  * denial.
968  */
969 TEST_F(trace, log_flags_subdomains_off)
970 {
971 	pid_t pid;
972 	int status;
973 	char *buf;
974 	char field[64];
975 
976 	ASSERT_EQ(0, tracefs_clear_buf());
977 
978 	pid = fork();
979 	ASSERT_LE(0, pid);
980 
981 	if (pid == 0) {
982 		struct landlock_ruleset_attr attr = {
983 			.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
984 		};
985 		int parent_fd, child_fd, dir_fd;
986 
987 		/* Parent domain with LOG_SUBDOMAINS_OFF. */
988 		parent_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
989 		if (parent_fd < 0)
990 			_exit(1);
991 
992 		prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
993 		if (landlock_restrict_self(
994 			    parent_fd,
995 			    LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF))
996 			_exit(1);
997 		close(parent_fd);
998 
999 		/* Child sub-domain with default flags. */
1000 		child_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
1001 		if (child_fd < 0)
1002 			_exit(1);
1003 
1004 		if (landlock_restrict_self(child_fd, 0))
1005 			_exit(1);
1006 		close(child_fd);
1007 
1008 		/* Trigger a denial from the child domain. */
1009 		dir_fd = open(".", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
1010 		if (dir_fd >= 0)
1011 			close(dir_fd);
1012 		_exit(0);
1013 	}
1014 
1015 	ASSERT_EQ(pid, waitpid(pid, &status, 0));
1016 	ASSERT_TRUE(WIFEXITED(status));
1017 	EXPECT_EQ(0, WEXITSTATUS(status));
1018 
1019 	buf = tracefs_read_buf();
1020 	ASSERT_NE(NULL, buf);
1021 
1022 	/*
1023 	 * Trace fires unconditionally even though audit is disabled for the
1024 	 * child domain (parent had LOG_SUBDOMAINS_OFF).
1025 	 */
1026 	EXPECT_LE(1,
1027 		  tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK)))
1028 	{
1029 		TH_LOG("Expected deny_access_fs event despite "
1030 		       "LOG_SUBDOMAINS_OFF\n%s",
1031 		       buf);
1032 	}
1033 
1034 	/*
1035 	 * The child's per-execution flags default to logging, but the
1036 	 * ancestor's LOG_SUBDOMAINS_OFF disables it, so audit suppresses this
1037 	 * denial (logged=0).  This is exactly the case the single logged field
1038 	 * captures and the raw per-execution flags could not.
1039 	 */
1040 	ASSERT_EQ(0,
1041 		  tracefs_extract_field(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK),
1042 					"logged", field, sizeof(field)));
1043 	EXPECT_STREQ("0", field);
1044 
1045 	free(buf);
1046 }
1047 
1048 /* Verifies that landlock_free_ruleset fires when a ruleset FD is closed. */
1049 TEST_F(trace, free_ruleset_on_close)
1050 {
1051 	struct landlock_ruleset_attr ruleset_attr = {
1052 		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
1053 	};
1054 	int ruleset_fd;
1055 	char *buf;
1056 
1057 	ruleset_fd =
1058 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
1059 	ASSERT_LE(0, ruleset_fd);
1060 
1061 	ASSERT_EQ(0, tracefs_clear_buf());
1062 
1063 	/* Closing the FD should trigger free_ruleset. */
1064 	close(ruleset_fd);
1065 
1066 	buf = tracefs_read_buf();
1067 	ASSERT_NE(NULL, buf);
1068 
1069 	EXPECT_EQ(1, tracefs_count_matches(buf, REGEX_FREE_RULESET(TRACE_TASK)))
1070 	{
1071 		TH_LOG("Expected 1 free_ruleset event\n%s", buf);
1072 	}
1073 
1074 	free(buf);
1075 }
1076 
1077 /*
1078  * Counts landlock_enforce_domain lines, filtered by @domain (NULL matches any),
1079  * @complete and @process_wide (a negative value matches any).  Builds the
1080  * anchored regex dynamically so a single helper covers every field assertion.
1081  */
1082 static int count_enforce_matches(const char *buf, const char *domain,
1083 				 int complete, int process_wide,
1084 				 int no_new_privs)
1085 {
1086 	char pattern[512], dom[80], comp[8], pw[8], nnp[8];
1087 
1088 	if (domain)
1089 		snprintf(dom, sizeof(dom), "%s", domain);
1090 	else
1091 		snprintf(dom, sizeof(dom), "[0-9a-f]\\+");
1092 	if (complete < 0)
1093 		snprintf(comp, sizeof(comp), "[01]");
1094 	else
1095 		snprintf(comp, sizeof(comp), "%d", complete);
1096 	if (process_wide < 0)
1097 		snprintf(pw, sizeof(pw), "[01]");
1098 	else
1099 		snprintf(pw, sizeof(pw), "%d", process_wide);
1100 	if (no_new_privs < 0)
1101 		snprintf(nnp, sizeof(nnp), "[01]");
1102 	else
1103 		snprintf(nnp, sizeof(nnp), "%d", no_new_privs);
1104 
1105 	snprintf(pattern, sizeof(pattern),
1106 		 TRACE_PREFIX(TRACE_TASK) "landlock_enforce_domain: "
1107 					  "domain=%s "
1108 					  "complete=%s process_wide=%s "
1109 					  "no_new_privs=%s$",
1110 		 dom, comp, pw, nnp);
1111 	return tracefs_count_matches(buf, pattern);
1112 }
1113 
1114 /* Idle sibling: waits on the barrier so it is a live thread, then sleeps. */
1115 static void *enforce_idle(void *arg)
1116 {
1117 	pthread_barrier_t *barrier = arg;
1118 
1119 	pthread_barrier_wait(barrier);
1120 	while (true)
1121 		sleep(1);
1122 	return NULL;
1123 }
1124 
1125 /*
1126  * Child body: spawns @nthreads idle siblings (barrier-synchronized so they are
1127  * live when the syscall runs), then enforces a domain with @flags.  Returns 0
1128  * on success; the process exits afterwards, reaping the siblings.
1129  */
1130 static int child_enforce(int nthreads, __u32 flags)
1131 {
1132 	pthread_t threads[8];
1133 	pthread_barrier_t barrier;
1134 	int ruleset_fd, i;
1135 
1136 	if (nthreads > 0) {
1137 		if (pthread_barrier_init(&barrier, NULL, nthreads + 1))
1138 			return 1;
1139 		for (i = 0; i < nthreads; i++)
1140 			if (pthread_create(&threads[i], NULL, enforce_idle,
1141 					   &barrier))
1142 				return 1;
1143 		pthread_barrier_wait(&barrier);
1144 	}
1145 
1146 	ruleset_fd = build_enforce_ruleset();
1147 	if (ruleset_fd < 0)
1148 		return 1;
1149 
1150 	/*
1151 	 * LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS sets no_new_privs itself, so skip
1152 	 * the prctl() to exercise that path; otherwise Landlock requires
1153 	 * no_new_privs up front.
1154 	 */
1155 	if (!(flags & LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS))
1156 		prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
1157 	if (landlock_restrict_self(ruleset_fd, flags))
1158 		return 1;
1159 	close(ruleset_fd);
1160 	return 0;
1161 }
1162 
1163 /*
1164  * Runs in a spawned thread after the group leader called pthread_exit().  The
1165  * leader lingers as an un-reaped zombie, so get_nr_threads() still counts it
1166  * and this non-leader is not the only thread; enforcing here therefore reports
1167  * process_wide=0.
1168  */
1169 static void *enforce_nonleader(void *arg)
1170 {
1171 	int ruleset_fd;
1172 
1173 	ruleset_fd = build_enforce_ruleset();
1174 	if (ruleset_fd < 0)
1175 		_exit(1);
1176 	prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
1177 	if (landlock_restrict_self(ruleset_fd, 0))
1178 		_exit(1);
1179 	_exit(0);
1180 }
1181 
1182 /*
1183  * Collapses the enforce_domain field cases into one parametrized test.  Each
1184  * variant runs child_enforce(nthreads, flags) and checks the resulting
1185  * enforce_domain events.  The flags column also selects how no_new_privs is
1186  * set: with LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS child_enforce() skips the
1187  * prctl() so the flag sets it (and, with TSYNC, propagates to the siblings);
1188  * otherwise a prior prctl() sets it on the caller (and TSYNC propagates that).
1189  */
1190 
1191 /* clang-format off */
1192 FIXTURE(trace_enforce) {
1193 	/* clang-format on */
1194 	int tracefs_ok;
1195 };
1196 
1197 FIXTURE_SETUP(trace_enforce)
1198 {
1199 	int ret;
1200 
1201 	set_cap(_metadata, CAP_SYS_ADMIN);
1202 	ASSERT_EQ(0, unshare(CLONE_NEWNS));
1203 	ASSERT_EQ(0, mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL));
1204 
1205 	ret = tracefs_fixture_setup();
1206 	if (ret) {
1207 		clear_cap(_metadata, CAP_SYS_ADMIN);
1208 		self->tracefs_ok = 0;
1209 		SKIP(return, "tracefs not available");
1210 	}
1211 	self->tracefs_ok = 1;
1212 
1213 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_CREATE_RULESET_ENABLE, true));
1214 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_CREATE_DOMAIN_ENABLE, true));
1215 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_ENFORCE_DOMAIN_ENABLE, true));
1216 	ASSERT_EQ(0, tracefs_clear());
1217 	clear_cap(_metadata, CAP_SYS_ADMIN);
1218 }
1219 
1220 FIXTURE_TEARDOWN(trace_enforce)
1221 {
1222 	if (!self->tracefs_ok)
1223 		return;
1224 
1225 	set_cap(_metadata, CAP_SYS_ADMIN);
1226 	tracefs_enable_event(TRACEFS_CREATE_RULESET_ENABLE, false);
1227 	tracefs_enable_event(TRACEFS_CREATE_DOMAIN_ENABLE, false);
1228 	tracefs_enable_event(TRACEFS_ENFORCE_DOMAIN_ENABLE, false);
1229 	tracefs_fixture_teardown();
1230 	clear_cap(_metadata, CAP_SYS_ADMIN);
1231 }
1232 
1233 /* clang-format off */
1234 FIXTURE_VARIANT(trace_enforce) {
1235 	/* clang-format on */
1236 	/* Inputs to child_enforce(). */
1237 	int nthreads;
1238 	__u32 flags;
1239 	/* Expected enforce_domain event counts. */
1240 	int total;
1241 	int complete;
1242 	int process_wide;
1243 	int no_new_privs;
1244 };
1245 
1246 /* clang-format off */
1247 
1248 /* Single thread, no flags: prctl-backed no_new_privs. */
1249 FIXTURE_VARIANT_ADD(trace_enforce, single) {
1250 	.nthreads = 0, .flags = 0,
1251 	.total = 1, .complete = 1, .process_wide = 1, .no_new_privs = 1,
1252 };
1253 
1254 /* Single thread: the NO_NEW_PRIVS flag sets no_new_privs (no prctl). */
1255 FIXTURE_VARIANT_ADD(trace_enforce, no_new_privs) {
1256 	.nthreads = 0, .flags = LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS,
1257 	.total = 1, .complete = 1, .process_wide = 1, .no_new_privs = 1,
1258 };
1259 
1260 /* TSYNC on a lone thread still concludes, process-wide. */
1261 FIXTURE_VARIANT_ADD(trace_enforce, tsync_single) {
1262 	.nthreads = 0, .flags = LANDLOCK_RESTRICT_SELF_TSYNC,
1263 	.total = 1, .complete = 1, .process_wide = 1, .no_new_privs = 1,
1264 };
1265 
1266 /* TSYNC sweeps N siblings; the caller's prctl-backed nnp propagates to all. */
1267 FIXTURE_VARIANT_ADD(trace_enforce, tsync_multithread) {
1268 	.nthreads = 3, .flags = LANDLOCK_RESTRICT_SELF_TSYNC,
1269 	.total = 4, .complete = 1, .process_wide = 4, .no_new_privs = 4,
1270 };
1271 
1272 /* TSYNC + NO_NEW_PRIVS flag sets nnp on the caller and every swept sibling. */
1273 FIXTURE_VARIANT_ADD(trace_enforce, tsync_no_new_privs) {
1274 	.nthreads = 3,
1275 	.flags = LANDLOCK_RESTRICT_SELF_TSYNC | LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS,
1276 	.total = 4, .complete = 1, .process_wide = 4, .no_new_privs = 4,
1277 };
1278 
1279 /* Non-TSYNC on a multi-threaded process enforces only the caller. */
1280 FIXTURE_VARIANT_ADD(trace_enforce, multithread_non_tsync) {
1281 	.nthreads = 3, .flags = 0,
1282 	.total = 1, .complete = 1, .process_wide = 0, .no_new_privs = 1,
1283 };
1284 
1285 /* clang-format on */
1286 
1287 /*
1288  * One create_domain and variant->total enforce_domain events sharing that
1289  * domain ID; complete=1 marks the single concluding event, and the process_wide
1290  * / no_new_privs counts match the variant.  Counts are order-independent,
1291  * evaluated after the syscall returns.
1292  */
1293 TEST_F(trace_enforce, enforce)
1294 {
1295 	pid_t pid;
1296 	int status;
1297 	char *buf;
1298 	char domain[64];
1299 
1300 	ASSERT_EQ(0, tracefs_clear_buf());
1301 
1302 	pid = fork();
1303 	ASSERT_LE(0, pid);
1304 	if (pid == 0)
1305 		_exit(child_enforce(variant->nthreads, variant->flags));
1306 
1307 	ASSERT_EQ(pid, waitpid(pid, &status, 0));
1308 	ASSERT_TRUE(WIFEXITED(status));
1309 	EXPECT_EQ(0, WEXITSTATUS(status));
1310 
1311 	buf = tracefs_read_buf();
1312 	ASSERT_NE(NULL, buf);
1313 
1314 	EXPECT_EQ(1,
1315 		  tracefs_count_matches(buf, REGEX_CREATE_DOMAIN(TRACE_TASK)));
1316 	EXPECT_EQ(variant->total, count_enforce_matches(buf, NULL, -1, -1, -1))
1317 	{
1318 		TH_LOG("Expected %d enforce_domain events\n%s", variant->total,
1319 		       buf);
1320 	}
1321 	EXPECT_EQ(variant->complete,
1322 		  count_enforce_matches(buf, NULL, 1, -1, -1));
1323 	EXPECT_EQ(variant->total - variant->complete,
1324 		  count_enforce_matches(buf, NULL, 0, -1, -1));
1325 	EXPECT_EQ(variant->process_wide,
1326 		  count_enforce_matches(buf, NULL, -1, 1, -1));
1327 	EXPECT_EQ(variant->total - variant->process_wide,
1328 		  count_enforce_matches(buf, NULL, -1, 0, -1));
1329 	EXPECT_EQ(variant->no_new_privs,
1330 		  count_enforce_matches(buf, NULL, -1, -1, 1));
1331 
1332 	ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CREATE_DOMAIN(TRACE_TASK),
1333 					   "domain", domain, sizeof(domain)));
1334 	EXPECT_EQ(variant->total,
1335 		  count_enforce_matches(buf, domain, -1, -1, -1));
1336 
1337 	free(buf);
1338 }
1339 
1340 /*
1341  * A non-leader thread enforcing a domain while the group leader lingers as an
1342  * un-reaped zombie reports process_wide=0: get_nr_threads() counts the zombie
1343  * leader, so the group is not single-threaded.  This is the reachable half of
1344  * the caveat that process_wide==0 never proves the process is multi-threaded
1345  * (get_nr_threads(), unlike the leader-relative thread_group_empty(), counts
1346  * the zombie leader).
1347  */
1348 TEST_F(trace, enforce_single_non_leader)
1349 {
1350 	pid_t pid;
1351 	int status;
1352 	char *buf;
1353 
1354 	ASSERT_EQ(0, tracefs_clear_buf());
1355 
1356 	pid = fork();
1357 	ASSERT_LE(0, pid);
1358 	if (pid == 0) {
1359 		pthread_t worker;
1360 
1361 		if (pthread_create(&worker, NULL, enforce_nonleader, NULL))
1362 			_exit(1);
1363 		/* Leader leaves; the worker enforces as a non-leader. */
1364 		pthread_exit(NULL);
1365 	}
1366 
1367 	ASSERT_EQ(pid, waitpid(pid, &status, 0));
1368 	ASSERT_TRUE(WIFEXITED(status));
1369 	EXPECT_EQ(0, WEXITSTATUS(status));
1370 
1371 	buf = tracefs_read_buf();
1372 	ASSERT_NE(NULL, buf);
1373 
1374 	EXPECT_EQ(1,
1375 		  tracefs_count_matches(buf, REGEX_CREATE_DOMAIN(TRACE_TASK)));
1376 	EXPECT_EQ(1, count_enforce_matches(buf, NULL, 1, 0, -1))
1377 	{
1378 		TH_LOG("Expected complete=1 process_wide=0 for non-leader\n%s",
1379 		       buf);
1380 	}
1381 
1382 	free(buf);
1383 }
1384 
1385 /*
1386  * Verifies the flags-only path (ruleset_fd == -1) creates no domain and emits
1387  * neither create_domain nor enforce_domain, with and without TSYNC.
1388  */
1389 TEST_F(trace, enforce_flags_only)
1390 {
1391 	pid_t pid;
1392 	int status;
1393 	char *buf;
1394 
1395 	ASSERT_EQ(0, tracefs_clear_buf());
1396 
1397 	pid = fork();
1398 	ASSERT_LE(0, pid);
1399 	if (pid == 0) {
1400 		prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
1401 		if (landlock_restrict_self(
1402 			    -1, LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF))
1403 			_exit(1);
1404 		if (landlock_restrict_self(
1405 			    -1, LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF |
1406 					LANDLOCK_RESTRICT_SELF_TSYNC))
1407 			_exit(1);
1408 		_exit(0);
1409 	}
1410 
1411 	ASSERT_EQ(pid, waitpid(pid, &status, 0));
1412 	ASSERT_TRUE(WIFEXITED(status));
1413 	EXPECT_EQ(0, WEXITSTATUS(status));
1414 
1415 	buf = tracefs_read_buf();
1416 	ASSERT_NE(NULL, buf);
1417 
1418 	EXPECT_EQ(0,
1419 		  tracefs_count_matches(buf, REGEX_CREATE_DOMAIN(TRACE_TASK)));
1420 	EXPECT_EQ(0, count_enforce_matches(buf, NULL, -1, -1, -1))
1421 	{
1422 		TH_LOG("No enforce_domain expected on flags-only path\n%s",
1423 		       buf);
1424 	}
1425 
1426 	free(buf);
1427 }
1428 
1429 static void enforce_nop_handler(int sig)
1430 {
1431 }
1432 
1433 struct abort_signaler_data {
1434 	pthread_t target;
1435 	volatile bool stop;
1436 };
1437 
1438 /*
1439  * Hammers the target thread with SIGUSR1 to interrupt the TSYNC prepare wait.
1440  */
1441 static void *abort_signaler(void *arg)
1442 {
1443 	struct abort_signaler_data *data = arg;
1444 
1445 	while (!data->stop)
1446 		pthread_kill(data->target, SIGUSR1);
1447 	return NULL;
1448 }
1449 
1450 /*
1451  * Child body for the abort test: with idle siblings and a signaler interrupting
1452  * it, repeatedly enforces under TSYNC.  An interrupted attempt aborts its
1453  * just-created domain (create_domain + free_domain, zero enforce_domain) while
1454  * -ERESTARTNOINTR transparently restarts the syscall, so a successful retry may
1455  * add its own full lifecycle.
1456  */
1457 static int child_abort(int nsiblings, int attempts)
1458 {
1459 	pthread_t threads[200];
1460 	pthread_t signaler;
1461 	pthread_barrier_t barrier;
1462 	struct abort_signaler_data data = {};
1463 	struct sigaction sa = {};
1464 	int i;
1465 
1466 	sa.sa_handler = enforce_nop_handler;
1467 	if (sigaction(SIGUSR1, &sa, NULL))
1468 		return 1;
1469 
1470 	if (pthread_barrier_init(&barrier, NULL, nsiblings + 1))
1471 		return 1;
1472 	for (i = 0; i < nsiblings; i++)
1473 		if (pthread_create(&threads[i], NULL, enforce_idle, &barrier))
1474 			return 1;
1475 	pthread_barrier_wait(&barrier);
1476 
1477 	data.target = pthread_self();
1478 	if (pthread_create(&signaler, NULL, abort_signaler, &data))
1479 		return 1;
1480 
1481 	prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
1482 	for (i = 0; i < attempts; i++) {
1483 		int ruleset_fd = build_enforce_ruleset();
1484 
1485 		if (ruleset_fd < 0)
1486 			break;
1487 		/*
1488 		 * Ignore the result: an abort returns an error, that is fine.
1489 		 */
1490 		landlock_restrict_self(ruleset_fd,
1491 				       LANDLOCK_RESTRICT_SELF_TSYNC);
1492 		close(ruleset_fd);
1493 	}
1494 
1495 	data.stop = true;
1496 	pthread_join(signaler, NULL);
1497 	return 0;
1498 }
1499 
1500 /*
1501  * Verifies the abort contract: a domain aborted by a thread-sync failure emits
1502  * create_domain and free_domain but zero enforce_domain.  The signal race is
1503  * probabilistic and -ERESTARTNOINTR may add a successful retry's lifecycle, so
1504  * events are grouped by domain ID and the test SKIPs if no abort occurred.
1505  */
1506 TEST_F(trace, enforce_abort)
1507 {
1508 	pid_t pid;
1509 	int status, retry;
1510 	char *buf = NULL;
1511 	const char *cursor;
1512 	char domain[64];
1513 	bool abort_found = false;
1514 
1515 	ASSERT_EQ(0, tracefs_clear_buf());
1516 
1517 	/* free_domain fires from a kworker, so widen the filter first. */
1518 	set_cap(_metadata, CAP_SYS_ADMIN);
1519 	tracefs_clear_pid_filter();
1520 	clear_cap(_metadata, CAP_SYS_ADMIN);
1521 
1522 	pid = fork();
1523 	ASSERT_LE(0, pid);
1524 	if (pid == 0)
1525 		/*
1526 		 * Match tsync_test's NUM_IDLE_THREADS: enough siblings that
1527 		 * credential preparation runs in several serialized waves,
1528 		 * giving the signaler a window to interrupt the thread-sync
1529 		 * wait and abort the operation.  A handful of threads finishes
1530 		 * in a single wave, leaving no window (the abort never fires).
1531 		 */
1532 		_exit(child_abort(200, 8));
1533 
1534 	ASSERT_EQ(pid, waitpid(pid, &status, 0));
1535 	ASSERT_TRUE(WIFEXITED(status));
1536 	EXPECT_EQ(0, WEXITSTATUS(status));
1537 
1538 	/* Poll for the asynchronous free_domain events. */
1539 	for (retry = 0; retry < 10; retry++) {
1540 		usleep(100000);
1541 		set_cap(_metadata, CAP_SYS_ADMIN);
1542 		free(buf);
1543 		buf = tracefs_read_trace();
1544 		clear_cap(_metadata, CAP_SYS_ADMIN);
1545 		ASSERT_NE(NULL, buf);
1546 	}
1547 
1548 	set_cap(_metadata, CAP_SYS_ADMIN);
1549 	ASSERT_EQ(0, tracefs_set_pid_filter(getpid()));
1550 	clear_cap(_metadata, CAP_SYS_ADMIN);
1551 
1552 	/*
1553 	 * Walk every create_domain and look for one whose domain ID has zero
1554 	 * enforce_domain events but a matching free_domain: that is an aborted
1555 	 * domain (created, never enforced, freed).
1556 	 */
1557 	cursor = buf;
1558 	while (tracefs_extract_field(cursor, REGEX_CREATE_DOMAIN(TRACE_TASK),
1559 				     "domain", domain, sizeof(domain)) == 0) {
1560 		const char *cd, *nl;
1561 		char free_pattern[256];
1562 
1563 		if (count_enforce_matches(buf, domain, -1, -1, -1) == 0) {
1564 			snprintf(
1565 				free_pattern, sizeof(free_pattern),
1566 				TRACE_PREFIX(
1567 					KWORKER_TASK) "landlock_free_domain: "
1568 						      "domain=%s denials=[0-9]\\+$",
1569 				domain);
1570 			if (tracefs_count_matches(buf, free_pattern) >= 1)
1571 				abort_found = true;
1572 		}
1573 
1574 		cd = strstr(cursor, "landlock_create_domain:");
1575 		if (!cd)
1576 			break;
1577 		nl = strchr(cd, '\n');
1578 		if (!nl)
1579 			break;
1580 		cursor = nl + 1;
1581 	}
1582 
1583 	if (!abort_found) {
1584 		free(buf);
1585 		SKIP(return, "signal race did not produce a thread-sync abort");
1586 	}
1587 
1588 	free(buf);
1589 }
1590 
1591 /*
1592  * The following tests are intentionally elided because the underlying kernel
1593  * mechanisms are already validated by audit tests:
1594  *
1595  * - Domain ID monotonicity: validated by audit_test.c:layers.  The same
1596  *   landlock_get_id_range() function serves both audit and trace.
1597  *
1598  * - Domain deallocation order (LIFO): validated by audit_test.c:layers.  Trace
1599  *   events fire from the same free_domain_work() code path.
1600  *
1601  * - Max-layer stacking (16 domains): validated by audit_test.c:layers.
1602  *
1603  * - IPv6 network tests: IPv6 hook dispatch uses the same
1604  *   current_check_access_socket() as IPv4, validated by net_test.c:audit tests.
1605  *
1606  * - Per-access-right full matrix (all 16 FS rights): hook dispatch is validated
1607  *   by fs_test.c:audit tests.  Trace tests verify representative samples to
1608  *   ensure bitmask encoding is correct.
1609  *
1610  * - Combined log flag variants (e.g., LOG_SUBDOMAINS_OFF + LOG_NEW_EXEC_ON):
1611  *   individual flag tests above cover each flag's effect on trace fields.  Flag
1612  *   combination logic is validated by audit_test.c:audit_flags tests.
1613  *
1614  * - fs.refer multi-record denials and fs.change_topology (mount):
1615  *   trace_denial() uses the same code path for all FS request types.  The
1616  *   DENTRY union member is validated by the deny_access_fs_fields
1617  *   test.  Audit tests in fs_test.c cover refer and mount denial specifics.
1618  */
1619 
1620 TEST_HARNESS_MAIN
1621