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