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_FS_ENABLE, true)); 125 ASSERT_EQ(0, tracefs_enable_event(TRACEFS_CHECK_RULE_FS_ENABLE, true)); 126 ASSERT_EQ(0, tracefs_enable_event(TRACEFS_DENY_ACCESS_FS_ENABLE, true)); 127 ASSERT_EQ(0, tracefs_clear()); 128 clear_cap(_metadata, CAP_SYS_ADMIN); 129 } 130 131 FIXTURE_TEARDOWN(trace_fs) 132 { 133 if (!self->tracefs_ok) 134 return; 135 136 set_cap(_metadata, CAP_SYS_ADMIN); 137 tracefs_enable_event(TRACEFS_ADD_RULE_FS_ENABLE, false); 138 tracefs_enable_event(TRACEFS_CHECK_RULE_FS_ENABLE, false); 139 tracefs_enable_event(TRACEFS_DENY_ACCESS_FS_ENABLE, false); 140 tracefs_fixture_teardown(); 141 clear_cap(_metadata, CAP_SYS_ADMIN); 142 } 143 144 /* 145 * Baseline: verifies that without Landlock, the operation succeeds and no 146 * check_rule or deny_access trace events fire. 147 */ 148 TEST_F(trace_fs, unsandboxed) 149 { 150 char *buf; 151 int count, status, fd; 152 pid_t pid; 153 154 ASSERT_EQ(0, tracefs_clear_buf()); 155 156 pid = fork(); 157 ASSERT_LE(0, pid); 158 159 if (pid == 0) { 160 /* 161 * No sandbox: verify that a normal FS access does not produce 162 * Landlock trace events. 163 */ 164 fd = open("/usr", O_RDONLY | O_DIRECTORY | O_CLOEXEC); 165 if (fd >= 0) 166 close(fd); 167 _exit(0); 168 } 169 170 ASSERT_EQ(pid, waitpid(pid, &status, 0)); 171 ASSERT_TRUE(WIFEXITED(status)); 172 EXPECT_EQ(0, WEXITSTATUS(status)); 173 174 buf = tracefs_read_buf(); 175 ASSERT_NE(NULL, buf); 176 177 count = tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK)); 178 EXPECT_EQ(0, count); 179 count = tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK)); 180 EXPECT_EQ(0, count); 181 182 free(buf); 183 } 184 185 /* 186 * Verifies that adding a filesystem rule emits a landlock_add_rule_fs trace 187 * event with the expected path and field values: ruleset ID is non-zero, 188 * access_rights is non-zero, and path matches. 189 */ 190 TEST_F(trace_fs, add_rule_fs) 191 { 192 struct landlock_ruleset_attr ruleset_attr = { 193 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | 194 LANDLOCK_ACCESS_FS_WRITE_FILE | 195 LANDLOCK_ACCESS_FS_READ_DIR, 196 }; 197 struct landlock_path_beneath_attr path_beneath = { 198 .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE, 199 }; 200 char *buf, field_buf[64]; 201 int ruleset_fd, count; 202 203 ruleset_fd = 204 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 205 ASSERT_LE(0, ruleset_fd); 206 207 path_beneath.parent_fd = open("/usr", O_PATH | O_DIRECTORY | O_CLOEXEC); 208 ASSERT_LE(0, path_beneath.parent_fd); 209 210 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 211 &path_beneath, 0)); 212 ASSERT_EQ(0, close(path_beneath.parent_fd)); 213 ASSERT_EQ(0, close(ruleset_fd)); 214 215 buf = tracefs_read_buf(); 216 ASSERT_NE(NULL, buf); 217 218 count = tracefs_count_matches(buf, REGEX_ADD_RULE_FS(TRACE_TASK)); 219 EXPECT_EQ(1, count) 220 { 221 TH_LOG("Expected 1 add_rule_fs event, got %d\n%s", count, buf); 222 } 223 224 /* Ruleset ID should be non-zero. */ 225 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_ADD_RULE_FS(TRACE_TASK), 226 "ruleset", field_buf, 227 sizeof(field_buf))); 228 EXPECT_STRNE("0", field_buf); 229 230 /* Access rights should be non-zero. */ 231 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_ADD_RULE_FS(TRACE_TASK), 232 "access_rights", field_buf, 233 sizeof(field_buf))); 234 EXPECT_STRNE("", field_buf); 235 236 /* Path should be /usr. */ 237 ASSERT_EQ(0, 238 tracefs_extract_field(buf, REGEX_ADD_RULE_FS(TRACE_TASK), 239 "path", field_buf, sizeof(field_buf))); 240 EXPECT_STREQ("/usr", field_buf); 241 242 free(buf); 243 } 244 245 /* 246 * Verifies that a path whose escaping exceeds the trace scratch sequence does 247 * not corrupt a sibling symbolic field. 248 */ 249 TEST_F(trace_fs, add_rule_fs_escaped_path_overflow) 250 { 251 static const char access_prefix[] = "execute|write_file|read_file|"; 252 static const char access_suffix[] = "|ioctl_dev|resolve_unix"; 253 struct landlock_ruleset_attr ruleset_attr = { 254 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 255 }; 256 struct landlock_path_beneath_attr path_beneath = { 257 .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE, 258 }; 259 char path[PATH_MAX]; 260 char *buf, field_buf[256]; 261 size_t field_len; 262 int ruleset_fd, count; 263 264 create_long_path(_metadata, path); 265 266 ruleset_fd = 267 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 268 ASSERT_LE(0, ruleset_fd); 269 path_beneath.parent_fd = open(path, O_PATH | O_DIRECTORY | O_CLOEXEC); 270 ASSERT_LE(0, path_beneath.parent_fd); 271 272 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 273 &path_beneath, 0)); 274 ASSERT_EQ(0, close(path_beneath.parent_fd)); 275 ASSERT_EQ(0, close(ruleset_fd)); 276 277 buf = tracefs_read_buf(); 278 ASSERT_NE(NULL, buf); 279 280 count = tracefs_count_matches(buf, REGEX_ADD_RULE_FS(TRACE_TASK)); 281 EXPECT_EQ(1, count) 282 { 283 TH_LOG("Expected 1 add_rule_fs event, got %d\n%s", count, buf); 284 } 285 286 /* 287 * The marker catches a full revert with any compiler. The symbolic 288 * field also catches scratch-sequence poisoning when the compiler 289 * evaluates the overflowing path first, as GCC currently does. 290 */ 291 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_ADD_RULE_FS(TRACE_TASK), 292 "access_rights", field_buf, 293 sizeof(field_buf))); 294 EXPECT_EQ(0, 295 strncmp(field_buf, access_prefix, sizeof(access_prefix) - 1)); 296 EXPECT_EQ(NULL, strstr(field_buf, "|refer|")); 297 field_len = strlen(field_buf); 298 ASSERT_LE(sizeof(access_suffix) - 1, field_len); 299 EXPECT_STREQ(access_suffix, 300 field_buf + field_len - (sizeof(access_suffix) - 1)); 301 expect_truncated_path(_metadata, buf, REGEX_ADD_RULE_FS(TRACE_TASK)); 302 303 free(buf); 304 } 305 306 /* 307 * Verifies that an overflowing denied path does not corrupt its sibling 308 * symbolic blockers field. 309 */ 310 TEST_F(trace_fs, deny_access_fs_escaped_path_overflow) 311 { 312 char path[PATH_MAX]; 313 char *buf, field_buf[64]; 314 int count, err; 315 316 create_long_path(_metadata, path); 317 ASSERT_EQ(0, tracefs_clear_buf()); 318 319 sandbox_child_fs_access(_metadata, "/usr", LANDLOCK_ACCESS_FS_READ_DIR, 320 LANDLOCK_ACCESS_FS_READ_DIR, path); 321 322 buf = tracefs_read_buf(); 323 ASSERT_NE(NULL, buf); 324 325 count = tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK)); 326 EXPECT_EQ(1, count) 327 { 328 TH_LOG("Expected 1 deny_access_fs event, got %d\n%s", count, 329 buf); 330 } 331 332 /* 333 * The marker catches a full revert with any compiler. The symbolic 334 * field also catches scratch-sequence poisoning when the compiler 335 * evaluates the overflowing path first, as GCC currently does. 336 */ 337 err = tracefs_extract_field(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK), 338 "blockers", field_buf, sizeof(field_buf)); 339 ASSERT_EQ(0, err); 340 EXPECT_STREQ("read_dir", field_buf); 341 expect_truncated_path(_metadata, buf, REGEX_DENY_ACCESS_FS(TRACE_TASK)); 342 343 free(buf); 344 } 345 346 /* 347 * Verifies that an allowed access emits check_rule events (rule matched during 348 * pathwalk) but does NOT emit deny_access events (no denial). 349 */ 350 TEST_F(trace_fs, allowed_access) 351 { 352 char *buf, field_buf[64]; 353 int count; 354 355 ASSERT_EQ(0, tracefs_clear_buf()); 356 357 /* Rule allows READ_DIR for /usr, access /usr which is allowed. */ 358 sandbox_child_fs_access(_metadata, "/usr", LANDLOCK_ACCESS_FS_READ_DIR, 359 LANDLOCK_ACCESS_FS_READ_DIR, "/usr"); 360 361 buf = tracefs_read_buf(); 362 ASSERT_NE(NULL, buf); 363 364 count = tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK)); 365 EXPECT_LE(1, count); 366 367 /* Single-layer grants array, intersected with the request. */ 368 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK), 369 "grants", field_buf, 370 sizeof(field_buf))); 371 EXPECT_STREQ("{read_dir}", field_buf); 372 373 count = tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK)); 374 EXPECT_EQ(0, count); 375 376 free(buf); 377 } 378 379 /* 380 * Verifies that accessing a path whose access type is not in the handled set 381 * does not emit landlock_check_rule events. The ruleset handles READ_FILE, but 382 * the directory open checks READ_DIR which is unhandled; Landlock has no 383 * opinion and no rule evaluation occurs. 384 */ 385 TEST_F(trace_fs, check_rule_unhandled) 386 { 387 char *buf; 388 int count; 389 390 ASSERT_EQ(0, tracefs_clear_buf()); 391 392 /* Handles READ_FILE only; READ_DIR is unhandled. */ 393 sandbox_child_fs_access(_metadata, "/usr", LANDLOCK_ACCESS_FS_READ_FILE, 394 LANDLOCK_ACCESS_FS_READ_FILE, "/tmp"); 395 396 buf = tracefs_read_buf(); 397 ASSERT_NE(NULL, buf); 398 399 /* No check_rule events because READ_DIR is not in the handled set. */ 400 count = tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK)); 401 EXPECT_EQ(0, count); 402 403 free(buf); 404 } 405 406 /* 407 * Verifies that nested domains (child sandboxed under a parent domain) emit 408 * check_rule events from both layers and produce a deny_access event when the 409 * inner domain's rule does not cover the access. 410 */ 411 TEST_F(trace_fs, check_rule_nested) 412 { 413 char *buf, field_buf[64], *comma; 414 size_t first_len, second_len; 415 int count_rule, count_access, status; 416 pid_t pid; 417 418 ASSERT_EQ(0, tracefs_clear_buf()); 419 420 pid = fork(); 421 ASSERT_LE(0, pid); 422 423 if (pid == 0) { 424 struct landlock_ruleset_attr ruleset_attr = { 425 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR, 426 }; 427 struct landlock_path_beneath_attr path_beneath = { 428 .allowed_access = LANDLOCK_ACCESS_FS_READ_DIR, 429 }; 430 int ruleset_fd, fd; 431 432 /* First layer: allow /usr. */ 433 ruleset_fd = landlock_create_ruleset(&ruleset_attr, 434 sizeof(ruleset_attr), 0); 435 if (ruleset_fd < 0) 436 _exit(1); 437 438 path_beneath.parent_fd = 439 open("/usr", O_PATH | O_DIRECTORY | O_CLOEXEC); 440 if (path_beneath.parent_fd < 0) { 441 close(ruleset_fd); 442 _exit(1); 443 } 444 445 if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 446 &path_beneath, 0)) { 447 close(path_beneath.parent_fd); 448 close(ruleset_fd); 449 _exit(1); 450 } 451 close(path_beneath.parent_fd); 452 453 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); 454 if (landlock_restrict_self(ruleset_fd, 0)) { 455 close(ruleset_fd); 456 _exit(1); 457 } 458 close(ruleset_fd); 459 460 /* Second layer: also allow /usr. */ 461 ruleset_fd = landlock_create_ruleset(&ruleset_attr, 462 sizeof(ruleset_attr), 0); 463 if (ruleset_fd < 0) 464 _exit(1); 465 466 path_beneath.parent_fd = 467 open("/usr", O_PATH | O_DIRECTORY | O_CLOEXEC); 468 if (path_beneath.parent_fd < 0) { 469 close(ruleset_fd); 470 _exit(1); 471 } 472 473 if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 474 &path_beneath, 0)) { 475 close(path_beneath.parent_fd); 476 close(ruleset_fd); 477 _exit(1); 478 } 479 close(path_beneath.parent_fd); 480 481 if (landlock_restrict_self(ruleset_fd, 0)) { 482 close(ruleset_fd); 483 _exit(1); 484 } 485 close(ruleset_fd); 486 487 /* Access /usr which is allowed by both layers. */ 488 fd = open("/usr", O_RDONLY | O_DIRECTORY | O_CLOEXEC); 489 if (fd >= 0) 490 close(fd); 491 492 /* Access /tmp which has no rule in either layer. */ 493 fd = open("/tmp", O_RDONLY | O_DIRECTORY | O_CLOEXEC); 494 if (fd >= 0) 495 close(fd); 496 497 _exit(0); 498 } 499 500 ASSERT_EQ(pid, waitpid(pid, &status, 0)); 501 ASSERT_TRUE(WIFEXITED(status)); 502 EXPECT_EQ(0, WEXITSTATUS(status)); 503 504 buf = tracefs_read_buf(); 505 ASSERT_NE(NULL, buf); 506 507 count_rule = 508 tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK)); 509 EXPECT_LE(1, count_rule); 510 511 /* 512 * Both layers have the same rule, so the grants array must have two 513 * identical symbolic entries, e.g. {read_dir,read_dir}. 514 */ 515 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK), 516 "grants", field_buf, 517 sizeof(field_buf))); 518 comma = strchr(field_buf, ','); 519 EXPECT_NE(0, !!comma); 520 if (comma) { 521 /* 522 * Verify both entries are identical: compare the substring 523 * before the comma with the substring after it (stripping the 524 * braces). 525 */ 526 first_len = comma - field_buf - 1; 527 second_len = strlen(comma + 1) - 1; 528 EXPECT_EQ(first_len, second_len); 529 EXPECT_EQ(0, strncmp(field_buf + 1, comma + 1, first_len)); 530 } 531 532 count_access = 533 tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK)); 534 EXPECT_LE(1, count_access); 535 536 free(buf); 537 } 538 539 /* 540 * Verifies that a denied FS access emits a landlock_deny_access_fs trace event 541 * with the blocked access and path. 542 */ 543 TEST_F(trace_fs, deny_access_fs_denied) 544 { 545 char *buf; 546 int count; 547 548 ASSERT_EQ(0, tracefs_clear_buf()); 549 550 /* 551 * Rule allows READ_DIR for /usr, but access /tmp which has no rule. 552 * READ_DIR access to /tmp is denied by absence and should emit a 553 * deny_access_fs event. 554 */ 555 sandbox_child_fs_access(_metadata, "/usr", LANDLOCK_ACCESS_FS_READ_DIR, 556 LANDLOCK_ACCESS_FS_READ_DIR, "/tmp"); 557 558 buf = tracefs_read_buf(); 559 ASSERT_NE(NULL, buf); 560 561 count = tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK)); 562 EXPECT_LE(1, count); 563 564 free(buf); 565 } 566 567 /* 568 * A denied FS access covered by a quiet rule (LANDLOCK_ADD_RULE_QUIET with the 569 * access listed in quiet_access_fs) still emits a landlock_deny_access_fs 570 * event, but with logged=0, the same audit-logging verdict audit would apply to 571 * suppress the record. 572 */ 573 TEST_F(trace_fs, deny_access_fs_quiet) 574 { 575 char *buf, field[64]; 576 pid_t pid; 577 int status; 578 579 ASSERT_EQ(0, tracefs_clear_buf()); 580 581 pid = fork(); 582 ASSERT_LE(0, pid); 583 if (pid == 0) { 584 struct landlock_ruleset_attr ruleset_attr = { 585 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR, 586 .quiet_access_fs = LANDLOCK_ACCESS_FS_READ_DIR, 587 }; 588 struct landlock_path_beneath_attr path_beneath = { 589 .allowed_access = 0, 590 }; 591 int ruleset_fd, fd; 592 593 ruleset_fd = landlock_create_ruleset(&ruleset_attr, 594 sizeof(ruleset_attr), 0); 595 if (ruleset_fd < 0) 596 _exit(1); 597 598 /* Marks /tmp quiet without granting any access. */ 599 path_beneath.parent_fd = 600 open("/tmp", O_PATH | O_DIRECTORY | O_CLOEXEC); 601 if (path_beneath.parent_fd < 0) { 602 close(ruleset_fd); 603 _exit(1); 604 } 605 if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 606 &path_beneath, LANDLOCK_ADD_RULE_QUIET)) { 607 close(path_beneath.parent_fd); 608 close(ruleset_fd); 609 _exit(1); 610 } 611 close(path_beneath.parent_fd); 612 613 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); 614 if (landlock_restrict_self(ruleset_fd, 0)) { 615 close(ruleset_fd); 616 _exit(1); 617 } 618 close(ruleset_fd); 619 620 /* Denied READ_DIR on the quiet /tmp: suppressed, logged=0. */ 621 fd = open("/tmp", O_RDONLY | O_DIRECTORY | O_CLOEXEC); 622 if (fd >= 0) 623 close(fd); 624 _exit(0); 625 } 626 ASSERT_EQ(pid, waitpid(pid, &status, 0)); 627 ASSERT_TRUE(WIFEXITED(status)); 628 EXPECT_EQ(0, WEXITSTATUS(status)); 629 630 buf = tracefs_read_buf(); 631 ASSERT_NE(NULL, buf); 632 633 /* The event fires with the suppressed verdict. */ 634 EXPECT_LE(1, tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS_LOGGED( 635 TRACE_TASK, "0"))); 636 /* The quiet rule must not leave the denial logged. */ 637 EXPECT_EQ(0, tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS_LOGGED( 638 TRACE_TASK, "1"))); 639 640 /* 641 * Quiet suppresses only the logged verdict: the rest of the denial 642 * event stays populated (non-zero domain, non-empty blockers). 643 */ 644 ASSERT_EQ(0, tracefs_extract_field( 645 buf, REGEX_DENY_ACCESS_FS_LOGGED(TRACE_TASK, "0"), 646 "domain", field, sizeof(field))); 647 EXPECT_STRNE("0", field); 648 ASSERT_EQ(0, tracefs_extract_field( 649 buf, REGEX_DENY_ACCESS_FS_LOGGED(TRACE_TASK, "0"), 650 "blockers", field, sizeof(field))); 651 EXPECT_STRNE("", field); 652 653 free(buf); 654 } 655 656 TEST_HARNESS_MAIN 657