xref: /linux/tools/testing/selftests/landlock/trace_fs_test.c (revision 7e170da2edec9fb0717543571cc14b0aabee2b46)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Landlock tests - Filesystem tracepoints
4  *
5  * Copyright © 2026 Cloudflare, Inc.
6  */
7 
8 #define _GNU_SOURCE
9 #include <assert.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <limits.h>
13 #include <linux/landlock.h>
14 #include <sched.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <sys/mount.h>
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include <unistd.h>
22 
23 #include "common.h"
24 #include "trace.h"
25 
26 #define TRACE_TASK "trace_fs_test"
27 
28 /* Mirrors TRACE_SEQ_SIZE, conservatively larger than the usable buffer. */
29 #define TRACE_SEQUENCE_SIZE 8192
30 #define OCTAL_ESCAPE_LEN 4
31 #define LONG_PATH_COMPONENT_COUNT 11
32 #define LONG_PATH_COMPONENT_LEN 240
33 #define LONG_PATH_LEN                                                \
34 	(LONG_PATH_COMPONENT_COUNT * (LONG_PATH_COMPONENT_LEN + 1) + \
35 	 sizeof("/tmp"))
36 #define LONG_ESCAPED_PATH_LEN \
37 	(LONG_PATH_COMPONENT_COUNT * LONG_PATH_COMPONENT_LEN * OCTAL_ESCAPE_LEN)
38 
39 static_assert(LONG_ESCAPED_PATH_LEN > TRACE_SEQUENCE_SIZE,
40 	      "escaped path must exceed the trace sequence");
41 static_assert(LONG_PATH_LEN < PATH_MAX, "path must fit in PATH_MAX");
42 
43 static void create_long_path(struct __test_metadata *const _metadata,
44 			     char *path)
45 {
46 	size_t path_len;
47 
48 	strcpy(path, "/tmp");
49 	path_len = strlen(path);
50 
51 	set_cap(_metadata, CAP_SYS_ADMIN);
52 	ASSERT_EQ(0, mount("tmpfs", "/tmp", "tmpfs", 0, NULL));
53 	clear_cap(_metadata, CAP_SYS_ADMIN);
54 
55 	for (int i = 0; i < LONG_PATH_COMPONENT_COUNT; i++) {
56 		path[path_len++] = '/';
57 		memset(path + path_len, ' ', LONG_PATH_COMPONENT_LEN);
58 		path_len += LONG_PATH_COMPONENT_LEN;
59 		path[path_len] = '\0';
60 		ASSERT_EQ(0, mkdir(path, 0700));
61 	}
62 }
63 
64 static void expect_truncated_path(struct __test_metadata *const _metadata,
65 				  const char *const trace,
66 				  const char *const event_regex)
67 {
68 	static const char marker[] = "\xe2\x80\xa6";
69 	char *path;
70 	size_t path_len;
71 
72 	path = malloc(TRACE_SEQUENCE_SIZE);
73 	ASSERT_NE(NULL, path);
74 	ASSERT_EQ(0, tracefs_extract_field(trace, event_regex, "path", path,
75 					   TRACE_SEQUENCE_SIZE));
76 	EXPECT_EQ(path, strstr(path, "/tmp/"));
77 	EXPECT_NE(NULL, strstr(path, "\\040"));
78 
79 	path_len = strlen(path);
80 	ASSERT_LE(sizeof(marker) - 1, path_len);
81 	EXPECT_STREQ(marker, path + path_len - (sizeof(marker) - 1));
82 	free(path);
83 }
84 
85 /*
86  * Like REGEX_DENY_ACCESS_FS(), but pins the logged field to a specific value
87  * ("0" or "1") so a test can tell a suppressed (quiet) denial from a logged
88  * one.  The tracepoint fires for every denial; logged carries the audit
89  * verdict.
90  */
91 #define REGEX_DENY_ACCESS_FS_LOGGED(task, log) \
92 	TRACE_PREFIX(task)                     \
93 	"landlock_deny_access_fs: "            \
94 	"domain=[0-9a-f]\\+ "                  \
95 	"same_exec=[01] "                      \
96 	"logged=" log " "                      \
97 	"blockers=[a-z_|]* "                   \
98 	"dev=[0-9]\\+:[0-9]\\+ "               \
99 	"ino=[0-9]\\+ "                        \
100 	"path=[^ ]*$"
101 
102 /* clang-format off */
103 FIXTURE(trace_fs) {
104 	/* clang-format on */
105 	int tracefs_ok;
106 };
107 
108 FIXTURE_SETUP(trace_fs)
109 {
110 	int ret;
111 
112 	set_cap(_metadata, CAP_SYS_ADMIN);
113 	ASSERT_EQ(0, unshare(CLONE_NEWNS));
114 	ASSERT_EQ(0, mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL));
115 
116 	ret = tracefs_fixture_setup();
117 	if (ret) {
118 		clear_cap(_metadata, CAP_SYS_ADMIN);
119 		self->tracefs_ok = 0;
120 		SKIP(return, "tracefs not available");
121 	}
122 	self->tracefs_ok = 1;
123 
124 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_ADD_RULE_PATH_BENEATH_ENABLE,
125 					  true));
126 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_CHECK_RULE_FS_ENABLE, true));
127 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_DENY_ACCESS_FS_ENABLE, true));
128 	ASSERT_EQ(0, tracefs_clear());
129 	clear_cap(_metadata, CAP_SYS_ADMIN);
130 }
131 
132 FIXTURE_TEARDOWN(trace_fs)
133 {
134 	if (!self->tracefs_ok)
135 		return;
136 
137 	set_cap(_metadata, CAP_SYS_ADMIN);
138 	tracefs_enable_event(TRACEFS_ADD_RULE_PATH_BENEATH_ENABLE, false);
139 	tracefs_enable_event(TRACEFS_CHECK_RULE_FS_ENABLE, false);
140 	tracefs_enable_event(TRACEFS_DENY_ACCESS_FS_ENABLE, false);
141 	tracefs_fixture_teardown();
142 	clear_cap(_metadata, CAP_SYS_ADMIN);
143 }
144 
145 /*
146  * Baseline: verifies that without Landlock, the operation succeeds and no
147  * check_rule or deny_access trace events fire.
148  */
149 TEST_F(trace_fs, unsandboxed)
150 {
151 	char *buf;
152 	int count, status, fd;
153 	pid_t pid;
154 
155 	ASSERT_EQ(0, tracefs_clear_buf());
156 
157 	pid = fork();
158 	ASSERT_LE(0, pid);
159 
160 	if (pid == 0) {
161 		/*
162 		 * No sandbox: verify that a normal FS access does not produce
163 		 * Landlock trace events.
164 		 */
165 		fd = open("/usr", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
166 		if (fd >= 0)
167 			close(fd);
168 		_exit(0);
169 	}
170 
171 	ASSERT_EQ(pid, waitpid(pid, &status, 0));
172 	ASSERT_TRUE(WIFEXITED(status));
173 	EXPECT_EQ(0, WEXITSTATUS(status));
174 
175 	buf = tracefs_read_buf();
176 	ASSERT_NE(NULL, buf);
177 
178 	count = tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK));
179 	EXPECT_EQ(0, count);
180 	count = tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK));
181 	EXPECT_EQ(0, count);
182 
183 	free(buf);
184 }
185 
186 /*
187  * Verifies that adding a filesystem rule emits a landlock_add_rule_path_beneath
188  * event with the expected path and field values: the ruleset ID and
189  * access_rights are non-zero, and the path matches.
190  */
191 TEST_F(trace_fs, add_rule_path_beneath)
192 {
193 	struct landlock_ruleset_attr ruleset_attr = {
194 		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE |
195 				     LANDLOCK_ACCESS_FS_WRITE_FILE |
196 				     LANDLOCK_ACCESS_FS_READ_DIR,
197 	};
198 	struct landlock_path_beneath_attr path_beneath = {
199 		.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
200 	};
201 	char *buf, field_buf[64];
202 	int ruleset_fd, count;
203 
204 	ruleset_fd =
205 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
206 	ASSERT_LE(0, ruleset_fd);
207 
208 	path_beneath.parent_fd = open("/usr", O_PATH | O_DIRECTORY | O_CLOEXEC);
209 	ASSERT_LE(0, path_beneath.parent_fd);
210 
211 	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
212 				       &path_beneath, 0));
213 	ASSERT_EQ(0, close(path_beneath.parent_fd));
214 	ASSERT_EQ(0, close(ruleset_fd));
215 
216 	buf = tracefs_read_buf();
217 	ASSERT_NE(NULL, buf);
218 
219 	count = tracefs_count_matches(buf,
220 				      REGEX_ADD_RULE_PATH_BENEATH(TRACE_TASK));
221 	EXPECT_EQ(1, count)
222 	{
223 		TH_LOG("Expected 1 add_rule_path_beneath event, got %d\n%s",
224 		       count, buf);
225 	}
226 
227 	/* Ruleset ID should be non-zero. */
228 	ASSERT_EQ(0, tracefs_extract_field(
229 			     buf, REGEX_ADD_RULE_PATH_BENEATH(TRACE_TASK),
230 			     "ruleset", field_buf, sizeof(field_buf)));
231 	EXPECT_STRNE("0", field_buf);
232 
233 	/* Access rights should be non-zero. */
234 	ASSERT_EQ(0, tracefs_extract_field(
235 			     buf, REGEX_ADD_RULE_PATH_BENEATH(TRACE_TASK),
236 			     "access_rights", field_buf, sizeof(field_buf)));
237 	EXPECT_STRNE("", field_buf);
238 
239 	/* Path should be /usr. */
240 	ASSERT_EQ(0, tracefs_extract_field(
241 			     buf, REGEX_ADD_RULE_PATH_BENEATH(TRACE_TASK),
242 			     "path", field_buf, sizeof(field_buf)));
243 	EXPECT_STREQ("/usr", field_buf);
244 
245 	free(buf);
246 }
247 
248 /*
249  * Verifies that a path whose escaping exceeds the trace scratch sequence does
250  * not corrupt a sibling symbolic field.
251  */
252 TEST_F(trace_fs, add_rule_path_beneath_escaped_path_overflow)
253 {
254 	static const char access_prefix[] = "execute|write_file|read_file|";
255 	static const char access_suffix[] = "|ioctl_dev|resolve_unix";
256 	struct landlock_ruleset_attr ruleset_attr = {
257 		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
258 	};
259 	struct landlock_path_beneath_attr path_beneath = {
260 		.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
261 	};
262 	char path[PATH_MAX];
263 	char *buf, field_buf[256];
264 	size_t field_len;
265 	int ruleset_fd, count;
266 
267 	create_long_path(_metadata, path);
268 
269 	ruleset_fd =
270 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
271 	ASSERT_LE(0, ruleset_fd);
272 	path_beneath.parent_fd = open(path, O_PATH | O_DIRECTORY | O_CLOEXEC);
273 	ASSERT_LE(0, path_beneath.parent_fd);
274 
275 	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
276 				       &path_beneath, 0));
277 	ASSERT_EQ(0, close(path_beneath.parent_fd));
278 	ASSERT_EQ(0, close(ruleset_fd));
279 
280 	buf = tracefs_read_buf();
281 	ASSERT_NE(NULL, buf);
282 
283 	count = tracefs_count_matches(buf,
284 				      REGEX_ADD_RULE_PATH_BENEATH(TRACE_TASK));
285 	EXPECT_EQ(1, count)
286 	{
287 		TH_LOG("Expected 1 add_rule_path_beneath event, got %d\n%s",
288 		       count, buf);
289 	}
290 
291 	/*
292 	 * The marker catches a full revert with any compiler.  The symbolic
293 	 * field also catches scratch-sequence poisoning when the compiler
294 	 * evaluates the overflowing path first, as GCC currently does.
295 	 */
296 	ASSERT_EQ(0, tracefs_extract_field(
297 			     buf, REGEX_ADD_RULE_PATH_BENEATH(TRACE_TASK),
298 			     "access_rights", field_buf, sizeof(field_buf)));
299 	EXPECT_EQ(0,
300 		  strncmp(field_buf, access_prefix, sizeof(access_prefix) - 1));
301 	EXPECT_EQ(NULL, strstr(field_buf, "|refer|"));
302 	field_len = strlen(field_buf);
303 	ASSERT_LE(sizeof(access_suffix) - 1, field_len);
304 	EXPECT_STREQ(access_suffix,
305 		     field_buf + field_len - (sizeof(access_suffix) - 1));
306 	expect_truncated_path(_metadata, buf,
307 			      REGEX_ADD_RULE_PATH_BENEATH(TRACE_TASK));
308 
309 	free(buf);
310 }
311 
312 /*
313  * Verifies that an overflowing denied path does not corrupt its sibling
314  * symbolic blockers field.
315  */
316 TEST_F(trace_fs, deny_access_fs_escaped_path_overflow)
317 {
318 	char path[PATH_MAX];
319 	char *buf, field_buf[64];
320 	int count, err;
321 
322 	create_long_path(_metadata, path);
323 	ASSERT_EQ(0, tracefs_clear_buf());
324 
325 	sandbox_child_fs_access(_metadata, "/usr", LANDLOCK_ACCESS_FS_READ_DIR,
326 				LANDLOCK_ACCESS_FS_READ_DIR, path);
327 
328 	buf = tracefs_read_buf();
329 	ASSERT_NE(NULL, buf);
330 
331 	count = tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK));
332 	EXPECT_EQ(1, count)
333 	{
334 		TH_LOG("Expected 1 deny_access_fs event, got %d\n%s", count,
335 		       buf);
336 	}
337 
338 	/*
339 	 * The marker catches a full revert with any compiler.  The symbolic
340 	 * field also catches scratch-sequence poisoning when the compiler
341 	 * evaluates the overflowing path first, as GCC currently does.
342 	 */
343 	err = tracefs_extract_field(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK),
344 				    "blockers", field_buf, sizeof(field_buf));
345 	ASSERT_EQ(0, err);
346 	EXPECT_STREQ("read_dir", field_buf);
347 	expect_truncated_path(_metadata, buf, REGEX_DENY_ACCESS_FS(TRACE_TASK));
348 
349 	free(buf);
350 }
351 
352 /*
353  * Verifies that an allowed access emits check_rule events (rule matched during
354  * pathwalk) but does NOT emit deny_access events (no denial).
355  */
356 TEST_F(trace_fs, allowed_access)
357 {
358 	char *buf, field_buf[64];
359 	int count;
360 
361 	ASSERT_EQ(0, tracefs_clear_buf());
362 
363 	/* Rule allows READ_DIR for /usr, access /usr which is allowed. */
364 	sandbox_child_fs_access(_metadata, "/usr", LANDLOCK_ACCESS_FS_READ_DIR,
365 				LANDLOCK_ACCESS_FS_READ_DIR, "/usr");
366 
367 	buf = tracefs_read_buf();
368 	ASSERT_NE(NULL, buf);
369 
370 	count = tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK));
371 	EXPECT_LE(1, count);
372 
373 	/* Single-layer grants array, intersected with the request. */
374 	ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK),
375 					   "grants", field_buf,
376 					   sizeof(field_buf)));
377 	EXPECT_STREQ("{read_dir}", field_buf);
378 
379 	count = tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK));
380 	EXPECT_EQ(0, count);
381 
382 	free(buf);
383 }
384 
385 /*
386  * Verifies that accessing a path whose access type is not in the handled set
387  * does not emit landlock_check_rule events.  The ruleset handles READ_FILE, but
388  * the directory open checks READ_DIR which is unhandled; Landlock has no
389  * opinion and no rule evaluation occurs.
390  */
391 TEST_F(trace_fs, check_rule_unhandled)
392 {
393 	char *buf;
394 	int count;
395 
396 	ASSERT_EQ(0, tracefs_clear_buf());
397 
398 	/* Handles READ_FILE only; READ_DIR is unhandled. */
399 	sandbox_child_fs_access(_metadata, "/usr", LANDLOCK_ACCESS_FS_READ_FILE,
400 				LANDLOCK_ACCESS_FS_READ_FILE, "/tmp");
401 
402 	buf = tracefs_read_buf();
403 	ASSERT_NE(NULL, buf);
404 
405 	/* No check_rule events because READ_DIR is not in the handled set. */
406 	count = tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK));
407 	EXPECT_EQ(0, count);
408 
409 	free(buf);
410 }
411 
412 /*
413  * Verifies that nested domains (child sandboxed under a parent domain) emit
414  * check_rule events from both layers and produce a deny_access event when the
415  * inner domain's rule does not cover the access.
416  */
417 TEST_F(trace_fs, check_rule_nested)
418 {
419 	char *buf, field_buf[64], *comma;
420 	size_t first_len, second_len;
421 	int count_rule, count_access, status;
422 	pid_t pid;
423 
424 	ASSERT_EQ(0, tracefs_clear_buf());
425 
426 	pid = fork();
427 	ASSERT_LE(0, pid);
428 
429 	if (pid == 0) {
430 		struct landlock_ruleset_attr ruleset_attr = {
431 			.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
432 		};
433 		struct landlock_path_beneath_attr path_beneath = {
434 			.allowed_access = LANDLOCK_ACCESS_FS_READ_DIR,
435 		};
436 		int ruleset_fd, fd;
437 
438 		/* First layer: allow /usr. */
439 		ruleset_fd = landlock_create_ruleset(&ruleset_attr,
440 						     sizeof(ruleset_attr), 0);
441 		if (ruleset_fd < 0)
442 			_exit(1);
443 
444 		path_beneath.parent_fd =
445 			open("/usr", O_PATH | O_DIRECTORY | O_CLOEXEC);
446 		if (path_beneath.parent_fd < 0) {
447 			close(ruleset_fd);
448 			_exit(1);
449 		}
450 
451 		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
452 				      &path_beneath, 0)) {
453 			close(path_beneath.parent_fd);
454 			close(ruleset_fd);
455 			_exit(1);
456 		}
457 		close(path_beneath.parent_fd);
458 
459 		prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
460 		if (landlock_restrict_self(ruleset_fd, 0)) {
461 			close(ruleset_fd);
462 			_exit(1);
463 		}
464 		close(ruleset_fd);
465 
466 		/* Second layer: also allow /usr. */
467 		ruleset_fd = landlock_create_ruleset(&ruleset_attr,
468 						     sizeof(ruleset_attr), 0);
469 		if (ruleset_fd < 0)
470 			_exit(1);
471 
472 		path_beneath.parent_fd =
473 			open("/usr", O_PATH | O_DIRECTORY | O_CLOEXEC);
474 		if (path_beneath.parent_fd < 0) {
475 			close(ruleset_fd);
476 			_exit(1);
477 		}
478 
479 		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
480 				      &path_beneath, 0)) {
481 			close(path_beneath.parent_fd);
482 			close(ruleset_fd);
483 			_exit(1);
484 		}
485 		close(path_beneath.parent_fd);
486 
487 		if (landlock_restrict_self(ruleset_fd, 0)) {
488 			close(ruleset_fd);
489 			_exit(1);
490 		}
491 		close(ruleset_fd);
492 
493 		/* Access /usr which is allowed by both layers. */
494 		fd = open("/usr", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
495 		if (fd >= 0)
496 			close(fd);
497 
498 		/* Access /tmp which has no rule in either layer. */
499 		fd = open("/tmp", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
500 		if (fd >= 0)
501 			close(fd);
502 
503 		_exit(0);
504 	}
505 
506 	ASSERT_EQ(pid, waitpid(pid, &status, 0));
507 	ASSERT_TRUE(WIFEXITED(status));
508 	EXPECT_EQ(0, WEXITSTATUS(status));
509 
510 	buf = tracefs_read_buf();
511 	ASSERT_NE(NULL, buf);
512 
513 	count_rule =
514 		tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK));
515 	EXPECT_LE(1, count_rule);
516 
517 	/*
518 	 * Both layers have the same rule, so the grants array must have two
519 	 * identical symbolic entries, e.g. {read_dir,read_dir}.
520 	 */
521 	ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK),
522 					   "grants", field_buf,
523 					   sizeof(field_buf)));
524 	comma = strchr(field_buf, ',');
525 	EXPECT_NE(0, !!comma);
526 	if (comma) {
527 		/*
528 		 * Verify both entries are identical: compare the substring
529 		 * before the comma with the substring after it (stripping the
530 		 * braces).
531 		 */
532 		first_len = comma - field_buf - 1;
533 		second_len = strlen(comma + 1) - 1;
534 		EXPECT_EQ(first_len, second_len);
535 		EXPECT_EQ(0, strncmp(field_buf + 1, comma + 1, first_len));
536 	}
537 
538 	count_access =
539 		tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK));
540 	EXPECT_LE(1, count_access);
541 
542 	free(buf);
543 }
544 
545 /*
546  * Verifies that a denied FS access emits a landlock_deny_access_fs trace event
547  * with the blocked access and path.
548  */
549 TEST_F(trace_fs, deny_access_fs_denied)
550 {
551 	const char *const event_regex = REGEX_DENY_ACCESS_FS(TRACE_TASK);
552 	char *buf, blockers[64];
553 	int count;
554 
555 	ASSERT_EQ(0, tracefs_clear_buf());
556 
557 	/*
558 	 * Rule allows READ_DIR for /usr, but access /tmp which has no rule.
559 	 * READ_DIR access to /tmp is denied by absence and should emit a
560 	 * deny_access_fs event.
561 	 */
562 	sandbox_child_fs_access(_metadata, "/usr", LANDLOCK_ACCESS_FS_READ_DIR,
563 				LANDLOCK_ACCESS_FS_READ_DIR, "/tmp");
564 
565 	buf = tracefs_read_buf();
566 	ASSERT_NE(NULL, buf);
567 
568 	count = tracefs_count_matches(buf, event_regex);
569 	EXPECT_EQ(1, count)
570 	{
571 		TH_LOG("Expected 1 access denial, got %d\n%s", count, buf);
572 	}
573 	ASSERT_EQ(0, tracefs_extract_field(buf, event_regex, "blockers",
574 					   blockers, sizeof(blockers)));
575 	EXPECT_STREQ("read_dir", blockers);
576 
577 	free(buf);
578 }
579 
580 /*
581  * Verifies that a denied mount reports the singleton topology blocker rather
582  * than an empty access mask.
583  */
584 TEST_F(trace_fs, deny_change_topology)
585 {
586 	const char *const event_regex = REGEX_DENY_ACCESS_FS(TRACE_TASK);
587 	const struct landlock_ruleset_attr ruleset_attr = {
588 		.handled_access_fs = LANDLOCK_ACCESS_FS_REFER,
589 	};
590 	char *buf, blockers[64];
591 	int count, ruleset_fd, status;
592 	pid_t pid;
593 
594 	ruleset_fd =
595 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
596 	ASSERT_LE(0, ruleset_fd);
597 	ASSERT_EQ(0, tracefs_clear_buf());
598 
599 	/* Ensure that Landlock is the only expected mount denial. */
600 	set_cap(_metadata, CAP_SYS_ADMIN);
601 	pid = fork();
602 	ASSERT_LE(0, pid);
603 	if (pid == 0) {
604 		if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
605 			close(ruleset_fd);
606 			_exit(1);
607 		}
608 		if (landlock_restrict_self(ruleset_fd, 0)) {
609 			close(ruleset_fd);
610 			_exit(2);
611 		}
612 		close(ruleset_fd);
613 
614 		if (mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL) != -1)
615 			_exit(3);
616 
617 		if (errno != EPERM)
618 			_exit(4);
619 
620 		_exit(0);
621 	}
622 	close(ruleset_fd);
623 	clear_cap(_metadata, CAP_SYS_ADMIN);
624 
625 	ASSERT_EQ(pid, waitpid(pid, &status, 0));
626 	ASSERT_TRUE(WIFEXITED(status));
627 	EXPECT_EQ(0, WEXITSTATUS(status));
628 
629 	buf = tracefs_read_buf();
630 	ASSERT_NE(NULL, buf);
631 	count = tracefs_count_matches(buf, event_regex);
632 	EXPECT_EQ(1, count)
633 	{
634 		TH_LOG("Expected 1 topology denial, got %d\n%s", count, buf);
635 	}
636 	ASSERT_EQ(0, tracefs_extract_field(buf, event_regex, "blockers",
637 					   blockers, sizeof(blockers)));
638 	EXPECT_STREQ("change_topology", blockers);
639 
640 	free(buf);
641 }
642 
643 /*
644  * A denied FS access covered by a quiet rule (LANDLOCK_ADD_RULE_QUIET with the
645  * access listed in quiet_access_fs) still emits a landlock_deny_access_fs
646  * event, but with logged=0, the same audit-logging verdict audit would apply to
647  * suppress the record.
648  */
649 TEST_F(trace_fs, deny_access_fs_quiet)
650 {
651 	char *buf, field[64];
652 	pid_t pid;
653 	int status;
654 
655 	ASSERT_EQ(0, tracefs_clear_buf());
656 
657 	pid = fork();
658 	ASSERT_LE(0, pid);
659 	if (pid == 0) {
660 		struct landlock_ruleset_attr ruleset_attr = {
661 			.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
662 			.quiet_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
663 		};
664 		struct landlock_path_beneath_attr path_beneath = {
665 			.allowed_access = 0,
666 		};
667 		int ruleset_fd, fd;
668 
669 		ruleset_fd = landlock_create_ruleset(&ruleset_attr,
670 						     sizeof(ruleset_attr), 0);
671 		if (ruleset_fd < 0)
672 			_exit(1);
673 
674 		/* Marks /tmp quiet without granting any access. */
675 		path_beneath.parent_fd =
676 			open("/tmp", O_PATH | O_DIRECTORY | O_CLOEXEC);
677 		if (path_beneath.parent_fd < 0) {
678 			close(ruleset_fd);
679 			_exit(1);
680 		}
681 		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
682 				      &path_beneath, LANDLOCK_ADD_RULE_QUIET)) {
683 			close(path_beneath.parent_fd);
684 			close(ruleset_fd);
685 			_exit(1);
686 		}
687 		close(path_beneath.parent_fd);
688 
689 		prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
690 		if (landlock_restrict_self(ruleset_fd, 0)) {
691 			close(ruleset_fd);
692 			_exit(1);
693 		}
694 		close(ruleset_fd);
695 
696 		/* Denied READ_DIR on the quiet /tmp: suppressed, logged=0. */
697 		fd = open("/tmp", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
698 		if (fd >= 0)
699 			close(fd);
700 		_exit(0);
701 	}
702 	ASSERT_EQ(pid, waitpid(pid, &status, 0));
703 	ASSERT_TRUE(WIFEXITED(status));
704 	EXPECT_EQ(0, WEXITSTATUS(status));
705 
706 	buf = tracefs_read_buf();
707 	ASSERT_NE(NULL, buf);
708 
709 	/* The event fires with the suppressed verdict. */
710 	EXPECT_LE(1, tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS_LOGGED(
711 							TRACE_TASK, "0")));
712 	/* The quiet rule must not leave the denial logged. */
713 	EXPECT_EQ(0, tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS_LOGGED(
714 							TRACE_TASK, "1")));
715 
716 	/*
717 	 * Quiet suppresses only the logged verdict: the rest of the denial
718 	 * event stays populated (non-zero domain, non-empty blockers).
719 	 */
720 	ASSERT_EQ(0, tracefs_extract_field(
721 			     buf, REGEX_DENY_ACCESS_FS_LOGGED(TRACE_TASK, "0"),
722 			     "domain", field, sizeof(field)));
723 	EXPECT_STRNE("0", field);
724 	ASSERT_EQ(0, tracefs_extract_field(
725 			     buf, REGEX_DENY_ACCESS_FS_LOGGED(TRACE_TASK, "0"),
726 			     "blockers", field, sizeof(field)));
727 	EXPECT_STRNE("", field);
728 
729 	free(buf);
730 }
731 
732 TEST_HARNESS_MAIN
733