Message ID | 20221227140244.1041292-2-jeffxu@google.com (mailing list archive) |
---|---|
State | Handled Elsewhere |
Headers | show |
Series | selftests/landlock: Fix selftest ptrace_test run fail | expand |
Please run clang-format-14 -i ptrace_test.c On 27/12/2022 15:02, jeffxu@chromium.org wrote: > From: Jeff Xu <jeffxu@google.com> > > Add check for yama setting for ptrace_test. > > Signed-off-by: Jeff Xu <jeffxu@google.com> > --- > .../testing/selftests/landlock/ptrace_test.c | 173 ++++++++++++------ > 1 file changed, 112 insertions(+), 61 deletions(-) > > diff --git a/tools/testing/selftests/landlock/ptrace_test.c b/tools/testing/selftests/landlock/ptrace_test.c > index c28ef98ff3ac..0056815deef8 100644 > --- a/tools/testing/selftests/landlock/ptrace_test.c > +++ b/tools/testing/selftests/landlock/ptrace_test.c > @@ -60,6 +60,23 @@ static int test_ptrace_read(const pid_t pid) > return 0; > } > > +static int get_yama_ptrace_scope(void) > +{ > + int ret = -1; > + char buf[2] = {}; > + int fd = open("/proc/sys/kernel/yama/ptrace_scope", O_RDONLY); > + > + if (fd < 0) > + return 0; > + > + if (read(fd, &buf, 1) < 0) > + return -1; > + > + ret = atoi(buf); > + close(fd); > + return ret; > +} > + > /* clang-format off */ > FIXTURE(hierarchy) {}; > /* clang-format on */ > @@ -69,6 +86,7 @@ FIXTURE_VARIANT(hierarchy) > const bool domain_both; > const bool domain_parent; > const bool domain_child; > + const int yama_ptrace_scope_max_supported; This is not used anymore. > }; > > /* > @@ -93,6 +111,7 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_without_domain) { > .domain_both = false, > .domain_parent = false, > .domain_child = false, > + .yama_ptrace_scope_max_supported = 0, > }; > > /* > @@ -110,6 +129,7 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_with_one_domain) { > .domain_both = false, > .domain_parent = false, > .domain_child = true, > + .yama_ptrace_scope_max_supported = 1, > }; > > /* > @@ -126,6 +146,7 @@ FIXTURE_VARIANT_ADD(hierarchy, deny_with_parent_domain) { > .domain_both = false, > .domain_parent = true, > .domain_child = false, > + .yama_ptrace_scope_max_supported = 0, > }; > > /* > @@ -143,6 +164,7 @@ FIXTURE_VARIANT_ADD(hierarchy, deny_with_sibling_domain) { > .domain_both = false, > .domain_parent = true, > .domain_child = true, > + .yama_ptrace_scope_max_supported = 2, > }; > > /* > @@ -160,6 +182,7 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_sibling_domain) { > .domain_both = true, > .domain_parent = false, > .domain_child = false, > + .yama_ptrace_scope_max_supported = 0, > }; > > /* > @@ -178,6 +201,7 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_with_nested_domain) { > .domain_both = true, > .domain_parent = false, > .domain_child = true, > + .yama_ptrace_scope_max_supported = 1, > }; > > /* > @@ -196,6 +220,7 @@ FIXTURE_VARIANT_ADD(hierarchy, deny_with_nested_and_parent_domain) { > .domain_both = true, > .domain_parent = true, > .domain_child = false, > + .yama_ptrace_scope_max_supported = 0, > }; > > /* > @@ -216,6 +241,7 @@ FIXTURE_VARIANT_ADD(hierarchy, deny_with_forked_domain) { > .domain_both = true, > .domain_parent = true, > .domain_child = true, > + .yama_ptrace_scope_max_supported = 2, > }; > > FIXTURE_SETUP(hierarchy) > @@ -232,8 +258,20 @@ TEST_F(hierarchy, trace) > pid_t child, parent; > int status, err_proc_read; > int pipe_child[2], pipe_parent[2]; > + int yama_ptrace_scope; > char buf_parent; > long ret; > + bool can_trace_child, can_trace_parent; > + > + yama_ptrace_scope = get_yama_ptrace_scope(); > + ASSERT_LE(0, yama_ptrace_scope); > + > + if (yama_ptrace_scope >= 3) > + SKIP(return, "Yama forbids any ptrace use (scope %d)", yama_ptrace_scope); > + > + can_trace_child = !variant->domain_parent && (yama_ptrace_scope < 2); > + can_trace_parent = !variant->domain_child && (yama_ptrace_scope < 1); > + TH_LOG("can_trace_child:%d, can_trace_parent:%d", can_trace_child, can_trace_parent); No need to log this because tests may already be marked as skipped. > > /* > * Removes all effective and permitted capabilities to not interfere > @@ -258,6 +296,7 @@ TEST_F(hierarchy, trace) > > ASSERT_EQ(0, close(pipe_parent[1])); > ASSERT_EQ(0, close(pipe_child[0])); > + > if (variant->domain_child) > create_domain(_metadata); > > @@ -265,43 +304,47 @@ TEST_F(hierarchy, trace) > ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); > > /* Tests PTRACE_ATTACH and PTRACE_MODE_READ on the parent. */ > - err_proc_read = test_ptrace_read(parent); > - ret = ptrace(PTRACE_ATTACH, parent, NULL, 0); > - if (variant->domain_child) { You only need to replace this check with !can_trace_parent. > - EXPECT_EQ(-1, ret); > - EXPECT_EQ(EPERM, errno); > - EXPECT_EQ(EACCES, err_proc_read); > - } else { > - EXPECT_EQ(0, ret); > - EXPECT_EQ(0, err_proc_read); > - } > - if (ret == 0) { > - ASSERT_EQ(parent, waitpid(parent, &status, 0)); > - ASSERT_EQ(1, WIFSTOPPED(status)); > - ASSERT_EQ(0, ptrace(PTRACE_DETACH, parent, NULL, 0)); > + if (can_trace_parent) { The previous check should be enough. > + err_proc_read = test_ptrace_read(parent); > + ret = ptrace(PTRACE_ATTACH, parent, NULL, 0); > + if (variant->domain_child) { > + EXPECT_EQ(-1, ret); > + EXPECT_EQ(EPERM, errno); > + EXPECT_EQ(EACCES, err_proc_read); > + } else { > + EXPECT_EQ(0, ret); > + EXPECT_EQ(0, err_proc_read); > + } > + if (ret == 0) { > + ASSERT_EQ(parent, waitpid(parent, &status, 0)); > + ASSERT_EQ(1, WIFSTOPPED(status)); > + ASSERT_EQ(0, ptrace(PTRACE_DETACH, parent, NULL, 0)); > + } > } > > /* Tests child PTRACE_TRACEME. */ > - ret = ptrace(PTRACE_TRACEME); > - if (variant->domain_parent) { Ditto, replace with !can_trace_child. > - EXPECT_EQ(-1, ret); > - EXPECT_EQ(EPERM, errno); > - } else { > - EXPECT_EQ(0, ret); > + if (can_trace_child) { The previous check should be enough. > + ret = ptrace(PTRACE_TRACEME); > + if (variant->domain_parent) { > + EXPECT_EQ(-1, ret); > + EXPECT_EQ(EPERM, errno); > + } else { > + EXPECT_EQ(0, ret); > + } > + > + /* > + * Signals that the PTRACE_ATTACH test is done and the > + * PTRACE_TRACEME test is ongoing. > + */ > + ASSERT_EQ(1, write(pipe_child[1], ".", 1)); > + > + if (!variant->domain_parent) > + ASSERT_EQ(0, raise(SIGSTOP)); > + > + /* Waits for the parent PTRACE_ATTACH test. */ > + ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); > } > > - /* > - * Signals that the PTRACE_ATTACH test is done and the > - * PTRACE_TRACEME test is ongoing. > - */ > - ASSERT_EQ(1, write(pipe_child[1], ".", 1)); > - > - if (!variant->domain_parent) { Ditto, replace with can_trace_child. > - ASSERT_EQ(0, raise(SIGSTOP)); > - } > - > - /* Waits for the parent PTRACE_ATTACH test. */ > - ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); Should not be removed. > _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); > return; > } > @@ -318,42 +361,50 @@ TEST_F(hierarchy, trace) > * Waits for the child to test PTRACE_ATTACH on the parent and start > * testing PTRACE_TRACEME. > */ > - ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1)); > - > - /* Tests child PTRACE_TRACEME. */ > - if (!variant->domain_parent) { ditto > - ASSERT_EQ(child, waitpid(child, &status, 0)); > - ASSERT_EQ(1, WIFSTOPPED(status)); > - ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); > - } else { > - /* The child should not be traced by the parent. */ > - EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0)); > - EXPECT_EQ(ESRCH, errno); > - } > + if (can_trace_child) { > + ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1)); > > - /* Tests PTRACE_ATTACH and PTRACE_MODE_READ on the child. */ > - err_proc_read = test_ptrace_read(child); > - ret = ptrace(PTRACE_ATTACH, child, NULL, 0); > - if (variant->domain_parent) { ditto > - EXPECT_EQ(-1, ret); > - EXPECT_EQ(EPERM, errno); > - EXPECT_EQ(EACCES, err_proc_read); > - } else { > - EXPECT_EQ(0, ret); > - EXPECT_EQ(0, err_proc_read); > - } > - if (ret == 0) { > - ASSERT_EQ(child, waitpid(child, &status, 0)); > - ASSERT_EQ(1, WIFSTOPPED(status)); > - ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); > + /* Tests child PTRACE_TRACEME. */ > + if (!variant->domain_parent) { > + ASSERT_EQ(child, waitpid(child, &status, 0)); > + ASSERT_EQ(1, WIFSTOPPED(status)); > + ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); > + } else { > + /* The child should not be traced by the parent. */ > + EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0)); > + EXPECT_EQ(ESRCH, errno); > + } > + > + /* Tests PTRACE_ATTACH and PTRACE_MODE_READ on the child. */ > + err_proc_read = test_ptrace_read(child); > + ret = ptrace(PTRACE_ATTACH, child, NULL, 0); > + if (variant->domain_parent) { > + EXPECT_EQ(-1, ret); > + EXPECT_EQ(EPERM, errno); > + EXPECT_EQ(EACCES, err_proc_read); > + } else { > + EXPECT_EQ(0, ret); > + EXPECT_EQ(0, err_proc_read); > + } > + if (ret == 0) { > + ASSERT_EQ(child, waitpid(child, &status, 0)); > + ASSERT_EQ(1, WIFSTOPPED(status)); > + ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); > + } > + > + /* Signals that the parent PTRACE_ATTACH test is done. */ > + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); > } > > - /* Signals that the parent PTRACE_ATTACH test is done. */ > - ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); ditto > ASSERT_EQ(child, waitpid(child, &status, 0)); > if (WIFSIGNALED(status) || !WIFEXITED(status) || > WEXITSTATUS(status) != EXIT_SUCCESS) > _metadata->passed = 0; > + > + if (yama_ptrace_scope > 0) > + SKIP(return, "Incomplete tests due to Yama restrictions (ptrace_scope:%d)", For consistency, please change to " (scope %d)" > + yama_ptrace_scope); > + > } > > TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/landlock/ptrace_test.c b/tools/testing/selftests/landlock/ptrace_test.c index c28ef98ff3ac..0056815deef8 100644 --- a/tools/testing/selftests/landlock/ptrace_test.c +++ b/tools/testing/selftests/landlock/ptrace_test.c @@ -60,6 +60,23 @@ static int test_ptrace_read(const pid_t pid) return 0; } +static int get_yama_ptrace_scope(void) +{ + int ret = -1; + char buf[2] = {}; + int fd = open("/proc/sys/kernel/yama/ptrace_scope", O_RDONLY); + + if (fd < 0) + return 0; + + if (read(fd, &buf, 1) < 0) + return -1; + + ret = atoi(buf); + close(fd); + return ret; +} + /* clang-format off */ FIXTURE(hierarchy) {}; /* clang-format on */ @@ -69,6 +86,7 @@ FIXTURE_VARIANT(hierarchy) const bool domain_both; const bool domain_parent; const bool domain_child; + const int yama_ptrace_scope_max_supported; }; /* @@ -93,6 +111,7 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_without_domain) { .domain_both = false, .domain_parent = false, .domain_child = false, + .yama_ptrace_scope_max_supported = 0, }; /* @@ -110,6 +129,7 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_with_one_domain) { .domain_both = false, .domain_parent = false, .domain_child = true, + .yama_ptrace_scope_max_supported = 1, }; /* @@ -126,6 +146,7 @@ FIXTURE_VARIANT_ADD(hierarchy, deny_with_parent_domain) { .domain_both = false, .domain_parent = true, .domain_child = false, + .yama_ptrace_scope_max_supported = 0, }; /* @@ -143,6 +164,7 @@ FIXTURE_VARIANT_ADD(hierarchy, deny_with_sibling_domain) { .domain_both = false, .domain_parent = true, .domain_child = true, + .yama_ptrace_scope_max_supported = 2, }; /* @@ -160,6 +182,7 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_sibling_domain) { .domain_both = true, .domain_parent = false, .domain_child = false, + .yama_ptrace_scope_max_supported = 0, }; /* @@ -178,6 +201,7 @@ FIXTURE_VARIANT_ADD(hierarchy, allow_with_nested_domain) { .domain_both = true, .domain_parent = false, .domain_child = true, + .yama_ptrace_scope_max_supported = 1, }; /* @@ -196,6 +220,7 @@ FIXTURE_VARIANT_ADD(hierarchy, deny_with_nested_and_parent_domain) { .domain_both = true, .domain_parent = true, .domain_child = false, + .yama_ptrace_scope_max_supported = 0, }; /* @@ -216,6 +241,7 @@ FIXTURE_VARIANT_ADD(hierarchy, deny_with_forked_domain) { .domain_both = true, .domain_parent = true, .domain_child = true, + .yama_ptrace_scope_max_supported = 2, }; FIXTURE_SETUP(hierarchy) @@ -232,8 +258,20 @@ TEST_F(hierarchy, trace) pid_t child, parent; int status, err_proc_read; int pipe_child[2], pipe_parent[2]; + int yama_ptrace_scope; char buf_parent; long ret; + bool can_trace_child, can_trace_parent; + + yama_ptrace_scope = get_yama_ptrace_scope(); + ASSERT_LE(0, yama_ptrace_scope); + + if (yama_ptrace_scope >= 3) + SKIP(return, "Yama forbids any ptrace use (scope %d)", yama_ptrace_scope); + + can_trace_child = !variant->domain_parent && (yama_ptrace_scope < 2); + can_trace_parent = !variant->domain_child && (yama_ptrace_scope < 1); + TH_LOG("can_trace_child:%d, can_trace_parent:%d", can_trace_child, can_trace_parent); /* * Removes all effective and permitted capabilities to not interfere @@ -258,6 +296,7 @@ TEST_F(hierarchy, trace) ASSERT_EQ(0, close(pipe_parent[1])); ASSERT_EQ(0, close(pipe_child[0])); + if (variant->domain_child) create_domain(_metadata); @@ -265,43 +304,47 @@ TEST_F(hierarchy, trace) ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); /* Tests PTRACE_ATTACH and PTRACE_MODE_READ on the parent. */ - err_proc_read = test_ptrace_read(parent); - ret = ptrace(PTRACE_ATTACH, parent, NULL, 0); - if (variant->domain_child) { - EXPECT_EQ(-1, ret); - EXPECT_EQ(EPERM, errno); - EXPECT_EQ(EACCES, err_proc_read); - } else { - EXPECT_EQ(0, ret); - EXPECT_EQ(0, err_proc_read); - } - if (ret == 0) { - ASSERT_EQ(parent, waitpid(parent, &status, 0)); - ASSERT_EQ(1, WIFSTOPPED(status)); - ASSERT_EQ(0, ptrace(PTRACE_DETACH, parent, NULL, 0)); + if (can_trace_parent) { + err_proc_read = test_ptrace_read(parent); + ret = ptrace(PTRACE_ATTACH, parent, NULL, 0); + if (variant->domain_child) { + EXPECT_EQ(-1, ret); + EXPECT_EQ(EPERM, errno); + EXPECT_EQ(EACCES, err_proc_read); + } else { + EXPECT_EQ(0, ret); + EXPECT_EQ(0, err_proc_read); + } + if (ret == 0) { + ASSERT_EQ(parent, waitpid(parent, &status, 0)); + ASSERT_EQ(1, WIFSTOPPED(status)); + ASSERT_EQ(0, ptrace(PTRACE_DETACH, parent, NULL, 0)); + } } /* Tests child PTRACE_TRACEME. */ - ret = ptrace(PTRACE_TRACEME); - if (variant->domain_parent) { - EXPECT_EQ(-1, ret); - EXPECT_EQ(EPERM, errno); - } else { - EXPECT_EQ(0, ret); + if (can_trace_child) { + ret = ptrace(PTRACE_TRACEME); + if (variant->domain_parent) { + EXPECT_EQ(-1, ret); + EXPECT_EQ(EPERM, errno); + } else { + EXPECT_EQ(0, ret); + } + + /* + * Signals that the PTRACE_ATTACH test is done and the + * PTRACE_TRACEME test is ongoing. + */ + ASSERT_EQ(1, write(pipe_child[1], ".", 1)); + + if (!variant->domain_parent) + ASSERT_EQ(0, raise(SIGSTOP)); + + /* Waits for the parent PTRACE_ATTACH test. */ + ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); } - /* - * Signals that the PTRACE_ATTACH test is done and the - * PTRACE_TRACEME test is ongoing. - */ - ASSERT_EQ(1, write(pipe_child[1], ".", 1)); - - if (!variant->domain_parent) { - ASSERT_EQ(0, raise(SIGSTOP)); - } - - /* Waits for the parent PTRACE_ATTACH test. */ - ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); return; } @@ -318,42 +361,50 @@ TEST_F(hierarchy, trace) * Waits for the child to test PTRACE_ATTACH on the parent and start * testing PTRACE_TRACEME. */ - ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1)); - - /* Tests child PTRACE_TRACEME. */ - if (!variant->domain_parent) { - ASSERT_EQ(child, waitpid(child, &status, 0)); - ASSERT_EQ(1, WIFSTOPPED(status)); - ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); - } else { - /* The child should not be traced by the parent. */ - EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0)); - EXPECT_EQ(ESRCH, errno); - } + if (can_trace_child) { + ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1)); - /* Tests PTRACE_ATTACH and PTRACE_MODE_READ on the child. */ - err_proc_read = test_ptrace_read(child); - ret = ptrace(PTRACE_ATTACH, child, NULL, 0); - if (variant->domain_parent) { - EXPECT_EQ(-1, ret); - EXPECT_EQ(EPERM, errno); - EXPECT_EQ(EACCES, err_proc_read); - } else { - EXPECT_EQ(0, ret); - EXPECT_EQ(0, err_proc_read); - } - if (ret == 0) { - ASSERT_EQ(child, waitpid(child, &status, 0)); - ASSERT_EQ(1, WIFSTOPPED(status)); - ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); + /* Tests child PTRACE_TRACEME. */ + if (!variant->domain_parent) { + ASSERT_EQ(child, waitpid(child, &status, 0)); + ASSERT_EQ(1, WIFSTOPPED(status)); + ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); + } else { + /* The child should not be traced by the parent. */ + EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0)); + EXPECT_EQ(ESRCH, errno); + } + + /* Tests PTRACE_ATTACH and PTRACE_MODE_READ on the child. */ + err_proc_read = test_ptrace_read(child); + ret = ptrace(PTRACE_ATTACH, child, NULL, 0); + if (variant->domain_parent) { + EXPECT_EQ(-1, ret); + EXPECT_EQ(EPERM, errno); + EXPECT_EQ(EACCES, err_proc_read); + } else { + EXPECT_EQ(0, ret); + EXPECT_EQ(0, err_proc_read); + } + if (ret == 0) { + ASSERT_EQ(child, waitpid(child, &status, 0)); + ASSERT_EQ(1, WIFSTOPPED(status)); + ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); + } + + /* Signals that the parent PTRACE_ATTACH test is done. */ + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); } - /* Signals that the parent PTRACE_ATTACH test is done. */ - ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); ASSERT_EQ(child, waitpid(child, &status, 0)); if (WIFSIGNALED(status) || !WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) _metadata->passed = 0; + + if (yama_ptrace_scope > 0) + SKIP(return, "Incomplete tests due to Yama restrictions (ptrace_scope:%d)", + yama_ptrace_scope); + } TEST_HARNESS_MAIN