[PATCH v1 2/2] Landlock: Signal scoping tests
Tahera Fahimi
fahimitahera at gmail.com
Fri Jul 5 21:21:43 UTC 2024
Signed-off-by: Tahera Fahimi <fahimitahera at gmail.com>
---
.../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
More information about the Linux-security-module-archive
mailing list