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