1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Landlock tests - Ptrace 4 * 5 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 * Copyright © 2019-2020 ANSSI 7 * Copyright © 2024-2025 Microsoft Corporation 8 */ 9 10 #define _GNU_SOURCE 11 #include <errno.h> 12 #include <fcntl.h> 13 #include <linux/landlock.h> 14 #include <sched.h> 15 #include <signal.h> 16 #include <sys/mount.h> 17 #include <sys/prctl.h> 18 #include <sys/ptrace.h> 19 #include <sys/types.h> 20 #include <sys/wait.h> 21 #include <unistd.h> 22 23 #include "audit.h" 24 #include "common.h" 25 #include "trace.h" 26 27 /* Copied from security/yama/yama_lsm.c */ 28 #define YAMA_SCOPE_DISABLED 0 29 #define YAMA_SCOPE_RELATIONAL 1 30 31 static void create_domain(struct __test_metadata *const _metadata) 32 { 33 int ruleset_fd; 34 struct landlock_ruleset_attr ruleset_attr = { 35 .handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_BLOCK, 36 }; 37 38 ruleset_fd = 39 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 40 EXPECT_LE(0, ruleset_fd) 41 { 42 TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 43 } 44 EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 45 EXPECT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 46 EXPECT_EQ(0, close(ruleset_fd)); 47 } 48 49 static int test_ptrace_read(const pid_t pid) 50 { 51 static const char path_template[] = "/proc/%d/environ"; 52 char procenv_path[sizeof(path_template) + 10]; 53 int procenv_path_size, fd; 54 55 procenv_path_size = snprintf(procenv_path, sizeof(procenv_path), 56 path_template, pid); 57 if (procenv_path_size >= sizeof(procenv_path)) 58 return E2BIG; 59 60 fd = open(procenv_path, O_RDONLY | O_CLOEXEC); 61 if (fd < 0) 62 return errno; 63 /* 64 * Mixing error codes from close(2) and open(2) should not lead to any 65 * (access type) confusion for this test. 66 */ 67 if (close(fd) != 0) 68 return errno; 69 return 0; 70 } 71 72 static int get_yama_ptrace_scope(void) 73 { 74 int ret; 75 char buf[2] = {}; 76 const int fd = open("/proc/sys/kernel/yama/ptrace_scope", O_RDONLY); 77 78 if (fd < 0) 79 return 0; 80 81 if (read(fd, buf, 1) < 0) { 82 close(fd); 83 return -1; 84 } 85 86 ret = atoi(buf); 87 close(fd); 88 return ret; 89 } 90 91 /* clang-format off */ 92 FIXTURE(scoped_domains) {}; 93 /* clang-format on */ 94 95 /* 96 * Test multiple tracing combinations between a parent process P1 and a child 97 * process P2. 98 * 99 * Yama's scoped ptrace is presumed disabled. If enabled, this optional 100 * restriction is enforced in addition to any Landlock check, which means that 101 * all P2 requests to trace P1 would be denied. 102 */ 103 #include "scoped_base_variants.h" 104 105 FIXTURE_SETUP(scoped_domains) 106 { 107 } 108 109 FIXTURE_TEARDOWN(scoped_domains) 110 { 111 } 112 113 /* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */ 114 TEST_F(scoped_domains, trace) 115 { 116 pid_t child, parent; 117 int status, err_proc_read; 118 int pipe_child[2], pipe_parent[2]; 119 int yama_ptrace_scope; 120 char buf_parent; 121 long ret; 122 bool can_read_child, can_trace_child, can_read_parent, can_trace_parent; 123 124 yama_ptrace_scope = get_yama_ptrace_scope(); 125 ASSERT_LE(0, yama_ptrace_scope); 126 127 if (yama_ptrace_scope > YAMA_SCOPE_DISABLED) 128 TH_LOG("Incomplete tests due to Yama restrictions (scope %d)", 129 yama_ptrace_scope); 130 131 /* 132 * can_read_child is true if a parent process can read its child 133 * process, which is only the case when the parent process is not 134 * isolated from the child with a dedicated Landlock domain. 135 */ 136 can_read_child = !variant->domain_parent; 137 138 /* 139 * can_trace_child is true if a parent process can trace its child 140 * process. This depends on two conditions: 141 * - The parent process is not isolated from the child with a dedicated 142 * Landlock domain. 143 * - Yama allows tracing children (up to YAMA_SCOPE_RELATIONAL). 144 */ 145 can_trace_child = can_read_child && 146 yama_ptrace_scope <= YAMA_SCOPE_RELATIONAL; 147 148 /* 149 * can_read_parent is true if a child process can read its parent 150 * process, which is only the case when the child process is not 151 * isolated from the parent with a dedicated Landlock domain. 152 */ 153 can_read_parent = !variant->domain_child; 154 155 /* 156 * can_trace_parent is true if a child process can trace its parent 157 * process. This depends on two conditions: 158 * - The child process is not isolated from the parent with a dedicated 159 * Landlock domain. 160 * - Yama is disabled (YAMA_SCOPE_DISABLED). 161 */ 162 can_trace_parent = can_read_parent && 163 yama_ptrace_scope <= YAMA_SCOPE_DISABLED; 164 165 /* 166 * Removes all effective and permitted capabilities to not interfere 167 * with cap_ptrace_access_check() in case of PTRACE_MODE_FSCREDS. 168 */ 169 drop_caps(_metadata); 170 171 parent = getpid(); 172 ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); 173 ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); 174 if (variant->domain_both) { 175 create_domain(_metadata); 176 if (!__test_passed(_metadata)) 177 /* Aborts before forking. */ 178 return; 179 } 180 181 child = fork(); 182 ASSERT_LE(0, child); 183 if (child == 0) { 184 char buf_child; 185 186 ASSERT_EQ(0, close(pipe_parent[1])); 187 ASSERT_EQ(0, close(pipe_child[0])); 188 if (variant->domain_child) 189 create_domain(_metadata); 190 191 /* Waits for the parent to be in a domain, if any. */ 192 ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); 193 194 /* Tests PTRACE_MODE_READ on the parent. */ 195 err_proc_read = test_ptrace_read(parent); 196 if (can_read_parent) { 197 EXPECT_EQ(0, err_proc_read); 198 } else { 199 EXPECT_EQ(EACCES, err_proc_read); 200 } 201 202 /* Tests PTRACE_ATTACH on the parent. */ 203 ret = ptrace(PTRACE_ATTACH, parent, NULL, 0); 204 if (can_trace_parent) { 205 EXPECT_EQ(0, ret); 206 } else { 207 EXPECT_EQ(-1, ret); 208 EXPECT_EQ(EPERM, errno); 209 } 210 if (ret == 0) { 211 ASSERT_EQ(parent, waitpid(parent, &status, 0)); 212 ASSERT_EQ(1, WIFSTOPPED(status)); 213 ASSERT_EQ(0, ptrace(PTRACE_DETACH, parent, NULL, 0)); 214 } 215 216 /* Tests child PTRACE_TRACEME. */ 217 ret = ptrace(PTRACE_TRACEME); 218 if (can_trace_child) { 219 EXPECT_EQ(0, ret); 220 } else { 221 EXPECT_EQ(-1, ret); 222 EXPECT_EQ(EPERM, errno); 223 } 224 225 /* 226 * Signals that the PTRACE_ATTACH test is done and the 227 * PTRACE_TRACEME test is ongoing. 228 */ 229 ASSERT_EQ(1, write(pipe_child[1], ".", 1)); 230 231 if (can_trace_child) { 232 ASSERT_EQ(0, raise(SIGSTOP)); 233 } 234 235 /* Waits for the parent PTRACE_ATTACH test. */ 236 ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); 237 _exit(_metadata->exit_code); 238 return; 239 } 240 241 ASSERT_EQ(0, close(pipe_child[1])); 242 ASSERT_EQ(0, close(pipe_parent[0])); 243 if (variant->domain_parent) 244 create_domain(_metadata); 245 246 /* Signals that the parent is in a domain, if any. */ 247 ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 248 249 /* 250 * Waits for the child to test PTRACE_ATTACH on the parent and start 251 * testing PTRACE_TRACEME. 252 */ 253 ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1)); 254 255 /* Tests child PTRACE_TRACEME. */ 256 if (can_trace_child) { 257 ASSERT_EQ(child, waitpid(child, &status, 0)); 258 ASSERT_EQ(1, WIFSTOPPED(status)); 259 ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); 260 } else { 261 /* The child should not be traced by the parent. */ 262 EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0)); 263 EXPECT_EQ(ESRCH, errno); 264 } 265 266 /* Tests PTRACE_MODE_READ on the child. */ 267 err_proc_read = test_ptrace_read(child); 268 if (can_read_child) { 269 EXPECT_EQ(0, err_proc_read); 270 } else { 271 EXPECT_EQ(EACCES, err_proc_read); 272 } 273 274 /* Tests PTRACE_ATTACH on the child. */ 275 ret = ptrace(PTRACE_ATTACH, child, NULL, 0); 276 if (can_trace_child) { 277 EXPECT_EQ(0, ret); 278 } else { 279 EXPECT_EQ(-1, ret); 280 EXPECT_EQ(EPERM, errno); 281 } 282 283 if (ret == 0) { 284 ASSERT_EQ(child, waitpid(child, &status, 0)); 285 ASSERT_EQ(1, WIFSTOPPED(status)); 286 ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); 287 } 288 289 /* Signals that the parent PTRACE_ATTACH test is done. */ 290 ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 291 ASSERT_EQ(child, waitpid(child, &status, 0)); 292 293 if (WIFSIGNALED(status) || !WIFEXITED(status) || 294 WEXITSTATUS(status) != EXIT_SUCCESS) 295 _metadata->exit_code = KSFT_FAIL; 296 } 297 298 static int matches_log_ptrace(struct __test_metadata *const _metadata, 299 int audit_fd, const pid_t opid) 300 { 301 static const char log_template[] = REGEX_LANDLOCK_PREFIX 302 " blockers=ptrace opid=%d ocomm=\"ptrace_test\"$"; 303 char log_match[sizeof(log_template) + 10]; 304 int log_match_len; 305 306 log_match_len = 307 snprintf(log_match, sizeof(log_match), log_template, opid); 308 if (log_match_len > sizeof(log_match)) 309 return -E2BIG; 310 311 return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match, 312 NULL); 313 } 314 315 FIXTURE(audit) 316 { 317 struct audit_filter audit_filter; 318 int audit_fd; 319 }; 320 321 FIXTURE_SETUP(audit) 322 { 323 disable_caps(_metadata); 324 set_cap(_metadata, CAP_AUDIT_CONTROL); 325 self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); 326 EXPECT_LE(0, self->audit_fd); 327 clear_cap(_metadata, CAP_AUDIT_CONTROL); 328 } 329 330 FIXTURE_TEARDOWN_PARENT(audit) 331 { 332 EXPECT_EQ(0, audit_cleanup(-1, NULL)); 333 } 334 335 /* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */ 336 TEST_F(audit, trace) 337 { 338 pid_t child; 339 int status; 340 int pipe_child[2], pipe_parent[2]; 341 int yama_ptrace_scope; 342 char buf_parent; 343 struct audit_records records; 344 345 /* Makes sure there is no superfluous logged records. */ 346 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 347 EXPECT_EQ(0, records.access); 348 EXPECT_EQ(0, records.domain); 349 350 yama_ptrace_scope = get_yama_ptrace_scope(); 351 ASSERT_LE(0, yama_ptrace_scope); 352 353 if (yama_ptrace_scope > YAMA_SCOPE_DISABLED) 354 TH_LOG("Incomplete tests due to Yama restrictions (scope %d)", 355 yama_ptrace_scope); 356 357 /* 358 * Removes all effective and permitted capabilities to not interfere 359 * with cap_ptrace_access_check() in case of PTRACE_MODE_FSCREDS. 360 */ 361 drop_caps(_metadata); 362 363 ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); 364 ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); 365 366 child = fork(); 367 ASSERT_LE(0, child); 368 if (child == 0) { 369 char buf_child; 370 371 ASSERT_EQ(0, close(pipe_parent[1])); 372 ASSERT_EQ(0, close(pipe_child[0])); 373 374 /* Waits for the parent to be in a domain, if any. */ 375 ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); 376 377 /* Tests child PTRACE_TRACEME. */ 378 EXPECT_EQ(-1, ptrace(PTRACE_TRACEME)); 379 EXPECT_EQ(EPERM, errno); 380 /* We should see the child process. */ 381 EXPECT_EQ(0, matches_log_ptrace(_metadata, self->audit_fd, 382 getpid())); 383 384 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 385 EXPECT_EQ(0, records.access); 386 /* Checks for a domain creation. */ 387 EXPECT_EQ(1, records.domain); 388 389 /* 390 * Signals that the PTRACE_ATTACH test is done and the 391 * PTRACE_TRACEME test is ongoing. 392 */ 393 ASSERT_EQ(1, write(pipe_child[1], ".", 1)); 394 395 /* Waits for the parent PTRACE_ATTACH test. */ 396 ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); 397 _exit(_metadata->exit_code); 398 return; 399 } 400 401 ASSERT_EQ(0, close(pipe_child[1])); 402 ASSERT_EQ(0, close(pipe_parent[0])); 403 create_domain(_metadata); 404 405 /* Signals that the parent is in a domain. */ 406 ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 407 408 /* 409 * Waits for the child to test PTRACE_ATTACH on the parent and start 410 * testing PTRACE_TRACEME. 411 */ 412 ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1)); 413 414 /* The child should not be traced by the parent. */ 415 EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0)); 416 EXPECT_EQ(ESRCH, errno); 417 418 /* Tests PTRACE_ATTACH on the child. */ 419 EXPECT_EQ(-1, ptrace(PTRACE_ATTACH, child, NULL, 0)); 420 EXPECT_EQ(EPERM, errno); 421 EXPECT_EQ(0, matches_log_ptrace(_metadata, self->audit_fd, child)); 422 423 /* Signals that the parent PTRACE_ATTACH test is done. */ 424 ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 425 ASSERT_EQ(child, waitpid(child, &status, 0)); 426 if (WIFSIGNALED(status) || !WIFEXITED(status) || 427 WEXITSTATUS(status) != EXIT_SUCCESS) 428 _metadata->exit_code = KSFT_FAIL; 429 430 /* Makes sure there is no superfluous logged records. */ 431 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 432 EXPECT_EQ(0, records.access); 433 EXPECT_EQ(0, records.domain); 434 } 435 436 /* Trace tests */ 437 438 /* clang-format off */ 439 FIXTURE(trace_ptrace) { 440 /* clang-format on */ 441 int tracefs_ok; 442 }; 443 444 FIXTURE_SETUP(trace_ptrace) 445 { 446 int ret; 447 448 set_cap(_metadata, CAP_SYS_ADMIN); 449 ASSERT_EQ(0, unshare(CLONE_NEWNS)); 450 ASSERT_EQ(0, mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)); 451 452 ret = tracefs_fixture_setup(); 453 if (ret) { 454 clear_cap(_metadata, CAP_SYS_ADMIN); 455 self->tracefs_ok = 0; 456 SKIP(return, "tracefs not available"); 457 } 458 self->tracefs_ok = 1; 459 460 ASSERT_EQ(0, tracefs_enable_event(TRACEFS_DENY_PTRACE_ENABLE, true)); 461 ASSERT_EQ(0, tracefs_clear()); 462 clear_cap(_metadata, CAP_SYS_ADMIN); 463 } 464 465 FIXTURE_TEARDOWN(trace_ptrace) 466 { 467 if (!self->tracefs_ok) 468 return; 469 470 set_cap(_metadata, CAP_SYS_ADMIN); 471 tracefs_enable_event(TRACEFS_DENY_PTRACE_ENABLE, false); 472 tracefs_fixture_teardown(); 473 clear_cap(_metadata, CAP_SYS_ADMIN); 474 } 475 476 /* clang-format off */ 477 FIXTURE_VARIANT(trace_ptrace) 478 { 479 /* clang-format on */ 480 bool sandbox; 481 bool sandbox_target; 482 int expect_denied; 483 }; 484 485 /* Denied: sandboxed child ptraces unsandboxed parent (tracee_domain=0). */ 486 /* clang-format off */ 487 FIXTURE_VARIANT_ADD(trace_ptrace, denied) { 488 /* clang-format on */ 489 .sandbox = true, 490 .sandbox_target = false, 491 .expect_denied = 1, 492 }; 493 494 /* 495 * Denied: sandboxed child ptraces a sandboxed parent, so the tracee is in a 496 * domain and tracee_domain= is non-zero. 497 */ 498 /* clang-format off */ 499 FIXTURE_VARIANT_ADD(trace_ptrace, denied_scoped_target) { 500 /* clang-format on */ 501 .sandbox = true, 502 .sandbox_target = true, 503 .expect_denied = 1, 504 }; 505 506 /* Allowed: unsandboxed child uses PTRACE_TRACEME. */ 507 /* clang-format off */ 508 FIXTURE_VARIANT_ADD(trace_ptrace, allowed) { 509 /* clang-format on */ 510 .sandbox = false, 511 .sandbox_target = false, 512 .expect_denied = 0, 513 }; 514 515 TEST_F(trace_ptrace, deny_ptrace) 516 { 517 char *buf, field[64], expected_pid[16]; 518 int count, status; 519 pid_t child, parent; 520 521 if (!self->tracefs_ok) 522 SKIP(return, "tracefs not available"); 523 524 parent = getpid(); 525 526 /* 527 * Set a known comm so the denied variant can verify both the trace line 528 * task name and the tracee_comm= field. 529 */ 530 prctl(PR_SET_NAME, "ll_trace_test"); 531 532 /* 533 * For the non-zero tracee_domain case, sandbox the parent (the tracee) 534 * before forking. The child inherits that domain and adds its own 535 * layer, so the child (tracer) is not an ancestor of the tracee and the 536 * ptrace is still denied, with tracee_domain= naming the parent's 537 * domain. 538 */ 539 if (variant->sandbox_target) 540 create_domain(_metadata); 541 542 child = fork(); 543 ASSERT_LE(0, child); 544 545 if (child == 0) { 546 if (variant->sandbox) { 547 struct landlock_ruleset_attr ruleset_attr = { 548 .scoped = LANDLOCK_SCOPE_SIGNAL, 549 }; 550 int ruleset_fd; 551 552 /* 553 * Any scope creates a domain. Ptrace denial checks 554 * domain ancestry, not specific flags. 555 */ 556 ruleset_fd = landlock_create_ruleset( 557 &ruleset_attr, sizeof(ruleset_attr), 0); 558 if (ruleset_fd < 0) 559 _exit(1); 560 561 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); 562 if (landlock_restrict_self(ruleset_fd, 0)) { 563 close(ruleset_fd); 564 _exit(1); 565 } 566 close(ruleset_fd); 567 568 /* PTRACE_ATTACH on unsandboxed parent: denied. */ 569 if (ptrace(PTRACE_ATTACH, parent, NULL, NULL) == 0) { 570 ptrace(PTRACE_DETACH, parent, NULL, NULL); 571 _exit(2); 572 } 573 if (errno != EPERM) 574 _exit(3); 575 } else { 576 /* No sandbox: ptrace should succeed. */ 577 if (ptrace(PTRACE_TRACEME) != 0) 578 _exit(1); 579 } 580 581 _exit(0); 582 } 583 584 ASSERT_EQ(child, waitpid(child, &status, 0)); 585 ASSERT_TRUE(WIFEXITED(status)); 586 EXPECT_EQ(0, WEXITSTATUS(status)); 587 588 buf = tracefs_read_buf(); 589 ASSERT_NE(NULL, buf); 590 591 count = tracefs_count_matches(buf, REGEX_DENY_PTRACE("ll_trace_test")); 592 if (variant->expect_denied) { 593 EXPECT_EQ(variant->expect_denied, count) 594 { 595 TH_LOG("Expected deny_ptrace event, got %d\n%s", count, 596 buf); 597 } 598 599 /* Verify tracee_pid is the parent's TGID. */ 600 snprintf(expected_pid, sizeof(expected_pid), "%d", parent); 601 ASSERT_EQ(0, tracefs_extract_field( 602 buf, REGEX_DENY_PTRACE("ll_trace_test"), 603 "tracee_pid", field, sizeof(field))); 604 EXPECT_STREQ(expected_pid, field); 605 606 /* Verify tracee_comm matches prctl(PR_SET_NAME). */ 607 ASSERT_EQ(0, tracefs_extract_field( 608 buf, REGEX_DENY_PTRACE("ll_trace_test"), 609 "tracee_comm", field, sizeof(field))); 610 EXPECT_STREQ("ll_trace_test", field); 611 612 /* 613 * Verify tracee_domain: 0 when the tracee is unsandboxed, 614 * non-zero when the tracee is in a domain. 615 */ 616 ASSERT_EQ(0, tracefs_extract_field( 617 buf, REGEX_DENY_PTRACE("ll_trace_test"), 618 "tracee_domain", field, sizeof(field))); 619 EXPECT_EQ(variant->sandbox_target, strcmp("0", field) != 0) 620 { 621 TH_LOG("Unexpected tracee_domain=%s", field); 622 } 623 } else { 624 EXPECT_EQ(0, count) 625 { 626 TH_LOG("Expected 0 deny_ptrace events, got %d\n%s", 627 count, buf); 628 } 629 } 630 631 free(buf); 632 } 633 634 /* clang-format off */ 635 FIXTURE(trace_ptrace_traceme) { 636 /* clang-format on */ 637 int tracefs_ok; 638 }; 639 640 FIXTURE_SETUP(trace_ptrace_traceme) 641 { 642 int ret; 643 644 set_cap(_metadata, CAP_SYS_ADMIN); 645 ASSERT_EQ(0, unshare(CLONE_NEWNS)); 646 ASSERT_EQ(0, mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)); 647 648 ret = tracefs_fixture_setup(); 649 if (ret) { 650 clear_cap(_metadata, CAP_SYS_ADMIN); 651 self->tracefs_ok = 0; 652 SKIP(return, "tracefs not available"); 653 } 654 self->tracefs_ok = 1; 655 656 ASSERT_EQ(0, tracefs_enable_event(TRACEFS_DENY_PTRACE_ENABLE, true)); 657 ASSERT_EQ(0, tracefs_clear()); 658 clear_cap(_metadata, CAP_SYS_ADMIN); 659 } 660 661 FIXTURE_TEARDOWN(trace_ptrace_traceme) 662 { 663 if (!self->tracefs_ok) 664 return; 665 666 set_cap(_metadata, CAP_SYS_ADMIN); 667 tracefs_enable_event(TRACEFS_DENY_PTRACE_ENABLE, false); 668 tracefs_fixture_teardown(); 669 clear_cap(_metadata, CAP_SYS_ADMIN); 670 } 671 672 /* clang-format off */ 673 FIXTURE_VARIANT(trace_ptrace_traceme) 674 { 675 /* clang-format on */ 676 bool sandbox_tracer; 677 bool sandbox_tracee; 678 int expect_denied; 679 }; 680 681 /* 682 * Denied: a sandboxed tracer cannot trace the unsandboxed child that asked to 683 * be traced with PTRACE_TRACEME (tracee_domain=0). 684 */ 685 /* clang-format off */ 686 FIXTURE_VARIANT_ADD(trace_ptrace_traceme, denied) { 687 /* clang-format on */ 688 .sandbox_tracer = true, 689 .sandbox_tracee = false, 690 .expect_denied = 1, 691 }; 692 693 /* 694 * Denied: a sandboxed child in its own domain asks to be traced by a tracer in 695 * an unrelated domain, so the tracee is in a domain and tracee_domain= is 696 * non-zero. 697 */ 698 /* clang-format off */ 699 FIXTURE_VARIANT_ADD(trace_ptrace_traceme, denied_scoped_tracee) { 700 /* clang-format on */ 701 .sandbox_tracer = true, 702 .sandbox_tracee = true, 703 .expect_denied = 1, 704 }; 705 706 /* Allowed: unsandboxed child uses PTRACE_TRACEME with an unsandboxed tracer. */ 707 /* clang-format off */ 708 FIXTURE_VARIANT_ADD(trace_ptrace_traceme, allowed) { 709 /* clang-format on */ 710 .sandbox_tracer = false, 711 .sandbox_tracee = false, 712 .expect_denied = 0, 713 }; 714 715 TEST_F(trace_ptrace_traceme, deny_ptrace) 716 { 717 char *buf, field[64], expected_pid[16]; 718 int count, status, sync_pipe[2]; 719 pid_t child; 720 721 if (!self->tracefs_ok) 722 SKIP(return, "tracefs not available"); 723 724 /* 725 * Set a known comm so the denied variant can verify both the trace line 726 * task name and the tracee_comm= field. The tracee is the current 727 * (child) task for PTRACE_TRACEME, so the child inherits this name. 728 */ 729 prctl(PR_SET_NAME, "ll_trace_test"); 730 731 ASSERT_EQ(0, pipe2(sync_pipe, O_CLOEXEC)); 732 733 child = fork(); 734 ASSERT_LE(0, child); 735 736 if (child == 0) { 737 char c; 738 739 close(sync_pipe[1]); 740 741 /* 742 * The tracee is the current task; for the non-zero 743 * tracee_domain case it sandboxes itself in its own domain, 744 * unrelated to the tracer's domain, so PTRACE_TRACEME is still 745 * denied and tracee_domain= names the child's own domain. 746 */ 747 if (variant->sandbox_tracee) 748 create_domain(_metadata); 749 750 /* Waits for the tracer (parent) to enter its domain, if any. */ 751 if (read(sync_pipe[0], &c, 1) != 1) 752 _exit(1); 753 close(sync_pipe[0]); 754 755 if (variant->expect_denied) { 756 if (ptrace(PTRACE_TRACEME) == 0) 757 _exit(2); 758 if (errno != EPERM) 759 _exit(3); 760 } else { 761 if (ptrace(PTRACE_TRACEME) != 0) 762 _exit(4); 763 /* Lets the tracer reap the trace-stop and detach. */ 764 raise(SIGSTOP); 765 } 766 767 _exit(0); 768 } 769 770 close(sync_pipe[0]); 771 772 /* 773 * For a denial, the proposed tracer must be in a domain that is not an 774 * ancestor of the tracee's domain. Sandboxing the parent after the 775 * fork gives it a domain unrelated to the child. 776 */ 777 if (variant->sandbox_tracer) 778 create_domain(_metadata); 779 780 /* Signals the child that the tracer is in its domain, if any. */ 781 ASSERT_EQ(1, write(sync_pipe[1], ".", 1)); 782 close(sync_pipe[1]); 783 784 if (!variant->expect_denied) { 785 /* PTRACE_TRACEME succeeded: reap the SIGSTOP and detach. */ 786 ASSERT_EQ(child, waitpid(child, &status, WUNTRACED)); 787 ASSERT_TRUE(WIFSTOPPED(status)); 788 ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); 789 } 790 791 ASSERT_EQ(child, waitpid(child, &status, 0)); 792 ASSERT_TRUE(WIFEXITED(status)); 793 EXPECT_EQ(0, WEXITSTATUS(status)); 794 795 buf = tracefs_read_buf(); 796 ASSERT_NE(NULL, buf); 797 798 count = tracefs_count_matches(buf, REGEX_DENY_PTRACE("ll_trace_test")); 799 if (variant->expect_denied) { 800 EXPECT_EQ(variant->expect_denied, count) 801 { 802 TH_LOG("Expected deny_ptrace event, got %d\n%s", count, 803 buf); 804 } 805 806 /* Verify tracee_pid is the child's TGID (the traced task). */ 807 snprintf(expected_pid, sizeof(expected_pid), "%d", child); 808 ASSERT_EQ(0, tracefs_extract_field( 809 buf, REGEX_DENY_PTRACE("ll_trace_test"), 810 "tracee_pid", field, sizeof(field))); 811 EXPECT_STREQ(expected_pid, field); 812 813 /* 814 * Verify tracee_domain: 0 when the tracee is unsandboxed, 815 * non-zero when the tracee is in a domain. 816 */ 817 ASSERT_EQ(0, tracefs_extract_field( 818 buf, REGEX_DENY_PTRACE("ll_trace_test"), 819 "tracee_domain", field, sizeof(field))); 820 EXPECT_EQ(variant->sandbox_tracee, strcmp("0", field) != 0) 821 { 822 TH_LOG("Unexpected tracee_domain=%s", field); 823 } 824 } else { 825 EXPECT_EQ(0, count) 826 { 827 TH_LOG("Expected 0 deny_ptrace events, got %d\n%s", 828 count, buf); 829 } 830 } 831 832 free(buf); 833 } 834 835 TEST_HARNESS_MAIN 836