|
26 | 26 | #define CLONE_AUTOREAP (1ULL << 34) |
27 | 27 | #endif |
28 | 28 |
|
| 29 | +#ifndef CLONE_NNP |
| 30 | +#define CLONE_NNP (1ULL << 35) |
| 31 | +#endif |
| 32 | + |
29 | 33 | static pid_t create_autoreap_child(int *pidfd) |
30 | 34 | { |
31 | 35 | struct __clone_args args = { |
@@ -493,4 +497,126 @@ TEST(autoreap_no_inherit) |
493 | 497 | close(pidfd); |
494 | 498 | } |
495 | 499 |
|
| 500 | +/* |
| 501 | + * Test that CLONE_NNP sets no_new_privs on the child. |
| 502 | + * The child checks via prctl(PR_GET_NO_NEW_PRIVS) and reports back. |
| 503 | + * The parent must NOT have no_new_privs set afterwards. |
| 504 | + */ |
| 505 | +TEST(nnp_sets_no_new_privs) |
| 506 | +{ |
| 507 | + struct __clone_args args = { |
| 508 | + .flags = CLONE_PIDFD | CLONE_AUTOREAP | CLONE_NNP, |
| 509 | + .exit_signal = 0, |
| 510 | + }; |
| 511 | + struct pidfd_info info = { .mask = PIDFD_INFO_EXIT }; |
| 512 | + int pidfd = -1, ret; |
| 513 | + struct pollfd pfd; |
| 514 | + pid_t pid; |
| 515 | + |
| 516 | + /* Ensure parent does not already have no_new_privs. */ |
| 517 | + ret = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0); |
| 518 | + ASSERT_EQ(ret, 0) { |
| 519 | + TH_LOG("Parent already has no_new_privs set, cannot run test"); |
| 520 | + } |
| 521 | + |
| 522 | + args.pidfd = ptr_to_u64(&pidfd); |
| 523 | + |
| 524 | + pid = sys_clone3(&args, sizeof(args)); |
| 525 | + if (pid < 0 && errno == EINVAL) |
| 526 | + SKIP(return, "CLONE_NNP not supported"); |
| 527 | + ASSERT_GE(pid, 0); |
| 528 | + |
| 529 | + if (pid == 0) { |
| 530 | + /* |
| 531 | + * Child: check no_new_privs. Exit 0 if set, 1 if not. |
| 532 | + */ |
| 533 | + ret = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0); |
| 534 | + _exit(ret == 1 ? 0 : 1); |
| 535 | + } |
| 536 | + |
| 537 | + ASSERT_GE(pidfd, 0); |
| 538 | + |
| 539 | + /* Parent must still NOT have no_new_privs. */ |
| 540 | + ret = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0); |
| 541 | + ASSERT_EQ(ret, 0) { |
| 542 | + TH_LOG("Parent got no_new_privs after creating CLONE_NNP child"); |
| 543 | + } |
| 544 | + |
| 545 | + /* Wait for child to exit. */ |
| 546 | + pfd.fd = pidfd; |
| 547 | + pfd.events = POLLIN; |
| 548 | + ret = poll(&pfd, 1, 5000); |
| 549 | + ASSERT_EQ(ret, 1); |
| 550 | + |
| 551 | + /* Verify child exited with 0 (no_new_privs was set). */ |
| 552 | + ret = ioctl(pidfd, PIDFD_GET_INFO, &info); |
| 553 | + ASSERT_EQ(ret, 0); |
| 554 | + ASSERT_TRUE(info.mask & PIDFD_INFO_EXIT); |
| 555 | + ASSERT_TRUE(WIFEXITED(info.exit_code)); |
| 556 | + ASSERT_EQ(WEXITSTATUS(info.exit_code), 0) { |
| 557 | + TH_LOG("Child did not have no_new_privs set"); |
| 558 | + } |
| 559 | + |
| 560 | + close(pidfd); |
| 561 | +} |
| 562 | + |
| 563 | +/* |
| 564 | + * Test that CLONE_NNP with CLONE_THREAD fails with EINVAL. |
| 565 | + */ |
| 566 | +TEST(nnp_rejects_thread) |
| 567 | +{ |
| 568 | + struct __clone_args args = { |
| 569 | + .flags = CLONE_NNP | CLONE_THREAD | |
| 570 | + CLONE_SIGHAND | CLONE_VM, |
| 571 | + .exit_signal = 0, |
| 572 | + }; |
| 573 | + pid_t pid; |
| 574 | + |
| 575 | + pid = sys_clone3(&args, sizeof(args)); |
| 576 | + ASSERT_EQ(pid, -1); |
| 577 | + ASSERT_EQ(errno, EINVAL); |
| 578 | +} |
| 579 | + |
| 580 | +/* |
| 581 | + * Test that a plain CLONE_AUTOREAP child does NOT get no_new_privs. |
| 582 | + * Only CLONE_NNP should set it. |
| 583 | + */ |
| 584 | +TEST(autoreap_no_new_privs_unset) |
| 585 | +{ |
| 586 | + struct pidfd_info info = { .mask = PIDFD_INFO_EXIT }; |
| 587 | + int pidfd = -1, ret; |
| 588 | + struct pollfd pfd; |
| 589 | + pid_t pid; |
| 590 | + |
| 591 | + pid = create_autoreap_child(&pidfd); |
| 592 | + if (pid < 0 && errno == EINVAL) |
| 593 | + SKIP(return, "CLONE_AUTOREAP not supported"); |
| 594 | + ASSERT_GE(pid, 0); |
| 595 | + |
| 596 | + if (pid == 0) { |
| 597 | + /* |
| 598 | + * Child: check no_new_privs. Exit 0 if NOT set, 1 if set. |
| 599 | + */ |
| 600 | + ret = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0); |
| 601 | + _exit(ret == 0 ? 0 : 1); |
| 602 | + } |
| 603 | + |
| 604 | + ASSERT_GE(pidfd, 0); |
| 605 | + |
| 606 | + pfd.fd = pidfd; |
| 607 | + pfd.events = POLLIN; |
| 608 | + ret = poll(&pfd, 1, 5000); |
| 609 | + ASSERT_EQ(ret, 1); |
| 610 | + |
| 611 | + ret = ioctl(pidfd, PIDFD_GET_INFO, &info); |
| 612 | + ASSERT_EQ(ret, 0); |
| 613 | + ASSERT_TRUE(info.mask & PIDFD_INFO_EXIT); |
| 614 | + ASSERT_TRUE(WIFEXITED(info.exit_code)); |
| 615 | + ASSERT_EQ(WEXITSTATUS(info.exit_code), 0) { |
| 616 | + TH_LOG("Plain autoreap child unexpectedly has no_new_privs"); |
| 617 | + } |
| 618 | + |
| 619 | + close(pidfd); |
| 620 | +} |
| 621 | + |
496 | 622 | TEST_HARNESS_MAIN |
0 commit comments