[PATCH v1 2/2] Landlock: Signal scoping tests
Tahera Fahimi <[email protected]>
| Newsgroups | dev.linux.lists.outreachy,org.kernel.vger.linux-kernel,org.kernel.vger.linux-security-module,org.kernel.vger.netdev |
|---|---|
| Message-ID | <70756a7b4ed5bd32f2d881aa1a0d1e7760ce4215.1720213293.git.fahimitahera@gmail.com> |
Signed-off-by: Tahera Fahimi <[email protected]> --- .../testing/selftests/landlock/ptrace_test.c | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) diff --git a/tools/testing/selftests/landlock/ptrace_test.c b/tools/testing/selftests/landlock/ptrace_test.c index a19db4d0b3bd..e092b67f8b67 100644 --- a/tools/testing/selftests/landlock/ptrace_test.c +++ b/tools/testing/selftests/landlock/ptrace_test.c @@ -17,6 +17,8 @@ #include <sys/wait.h> #include <unistd.h> +#include <signal.h> + #include "common.h" /* Copied from security/yama/yama_lsm.c */ @@ -25,6 +27,8 @@ #define YAMA_SCOPE_CAPABILITY 2 #define YAMA_SCOPE_NO_ATTACH 3 +static sig_atomic_t signaled; + static void create_domain(struct __test_metadata *const _metadata) { int ruleset_fd; @@ -436,4 +440,216 @@ TEST_F(hierarchy, trace) _metadata->exit_code = KSFT_FAIL; } +static void create_sig_domain(struct __test_metadata *const _metadata) +{ + int ruleset_fd; + const struct landlock_ruleset_attr ruleset_attr = { + .scoped = LANDLOCK_SCOPED_SIGNAL, + }; + + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + EXPECT_LE(0, ruleset_fd) + { + TH_LOG("Failed to create a ruleset: %s", strerror(errno)); + } + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); +} + +static void scope_signal_handler(int sig, siginfo_t *info, void *ucontext) +{ + if (sig == SIGHUP || sig == SIGURG || sig == SIGTSTP || sig == SIGTRAP) + signaled = 1; + + // signal process group + //kill(-(t->pid), SIGKILL); +} + +/* clang-format off */ +FIXTURE(signal_scoping) {}; +/* clang-format on */ + +FIXTURE_VARIANT(signal_scoping) +{ + const int sig; + const bool domain_both; + const bool domain_parent; + const bool domain_child; +}; + +/* Default Action: Terminate*/ +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, deny_with_forked_domain) { + /* clang-format on */ + .sig = SIGHUP, + .domain_both = true, + .domain_parent = true, + .domain_child = true, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, allow_with_forked_domain) { + /* clang-format on */ + .sig = SIGHUP, + .domain_both = false, + .domain_parent = true, + .domain_child = false, +}; + +/* Default Action: Ignore*/ +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, deny_with_forked_domain_SIGURG) { + /* clang-format on */ + .sig = SIGURG, + .domain_both = true, + .domain_parent = true, + .domain_child = true, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, allow_with_forked_domain_SIGURG) { + /* clang-format on */ + .sig = SIGURG, + .domain_both = false, + .domain_parent = true, + .domain_child = false, +}; + +/* Default Action: Stop*/ +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, deny_with_forked_domain_SIGTSTP) { + /* clang-format on */ + .sig = SIGTSTP, + .domain_both = true, + .domain_parent = true, + .domain_child = true, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, allow_with_forked_domain_SIGTSTP) { + /* clang-format on */ + .sig = SIGTSTP, + .domain_both = false, + .domain_parent = true, + .domain_child = false, +}; + +/* Default Action: Coredump*/ +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, deny_with_forked_domain_SIGTRAP) { + /* clang-format on */ + .sig = SIGTRAP, + .domain_both = true, + .domain_parent = true, + .domain_child = true, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, allow_with_forked_domain_SIGTRAP) { + /* clang-format on */ + .sig = SIGTRAP, + .domain_both = false, + .domain_parent = true, + .domain_child = false, +}; + +FIXTURE_SETUP(signal_scoping) +{ +} + +FIXTURE_TEARDOWN(signal_scoping) +{ +} + +TEST_F(signal_scoping, test_signal) +{ + pid_t child; + pid_t parent = getpid(); + int status; + bool can_signal; + int pipe_child[2], pipe_parent[2]; + //char buf_parent; + + struct sigaction action = { + .sa_sigaction = scope_signal_handler, + .sa_flags = SA_SIGINFO, + + }; + + can_signal = !variant->domain_child; + + //sigemptyset(&act.sa_mask); + + ASSERT_LE(0, sigaction(variant->sig, &action, NULL)) + { + TH_LOG("ERROR in sigaction %s", strerror(errno)); + } + + if (variant->domain_both) { + create_sig_domain(_metadata); + if (!__test_passed(_metadata)) + /* Aborts before forking. */ + return; + } + ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); + + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + char buf_child; + int err; + + ASSERT_EQ(0, close(pipe_parent[1])); + ASSERT_EQ(0, close(pipe_child[0])); + + if (variant->domain_child) + create_sig_domain(_metadata); + + /* Waits for the parent to be in a domain, if any. */ + ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); + + //err = raise(SIGHUP); + err = kill(parent, variant->sig); + if (can_signal) { + ASSERT_EQ(0, err); + } else { + ASSERT_EQ(EPERM, errno) + { + TH_LOG("Invalid error cached: %s", + strerror(errno)); + } + } + _exit(_metadata->exit_code); + return; + } + + ASSERT_EQ(0, close(pipe_child[1])); + ASSERT_EQ(0, close(pipe_parent[0])); + if (variant->domain_parent) + create_sig_domain(_metadata); + + /* Signals that the parent is in a domain, if any. */ + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); + + if (can_signal) { + ASSERT_EQ(-1, pause()); + ASSERT_EQ(EINTR, errno); + ASSERT_EQ(1, signaled); + } + + ASSERT_EQ(child, waitpid(child, &status, 0)); + + if (WIFEXITED(status)) { + TH_LOG("Exited with code %d:", WEXITSTATUS(status)); + if (!can_signal) + ASSERT_NE(1, signaled); + } + + if (WIFSIGNALED(status) || !WIFEXITED(status) || + WEXITSTATUS(status) != EXIT_SUCCESS) + _metadata->exit_code = KSFT_FAIL; +} + TEST_HARNESS_MAIN -- 2.34.1