lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Message-Id: <15dc202bb7f0a462ddeaa0c1cd630d2a7c6fa5c5.1725657728.git.fahimitahera@gmail.com> Date: Fri, 6 Sep 2024 15:30:04 -0600 From: Tahera Fahimi <fahimitahera@...il.com> To: outreachy@...ts.linux.dev Cc: mic@...ikod.net, gnoack@...gle.com, paul@...l-moore.com, jmorris@...ei.org, serge@...lyn.com, linux-security-module@...r.kernel.org, linux-kernel@...r.kernel.org, bjorn3_gh@...tonmail.com, jannh@...gle.com, netdev@...r.kernel.org, Tahera Fahimi <fahimitahera@...il.com> Subject: [PATCH v4 2/6] selftest/landlock: Signal restriction tests This patch expands Landlock ABI version 6 by providing tests for signal scoping mechanism. Base on kill(2), if the signal is 0, no signal will be sent, but the permission of a process to send a signal will be checked. Likewise, this test consider one signal for each signal category (SIGTRAP, SIGURG, SIGHUP, and SIGTSTP). Signed-off-by: Tahera Fahimi <fahimitahera@...il.com> --- Changes in versions: V4: * Refactoring by providing two sets of tests, send_sig_to_parent to check simple case of sending signal with scoped and non-scoped domains, and check_access_signal to examine access to send a signal with various domains. V3: * Using generalized scoped domain creation "create_scoped_domain" V2: * Moving tests from ptrace_test.c to scoped_signal_test.c * Remove debugging statements. * Covering all basic restriction scenarios by sending 0 as signal V1: * Expanding Landlock ABI version 6 by providing basic tests for four signals to test signal scoping mechanism. --- .../selftests/landlock/scoped_signal_test.c | 225 ++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 tools/testing/selftests/landlock/scoped_signal_test.c diff --git a/tools/testing/selftests/landlock/scoped_signal_test.c b/tools/testing/selftests/landlock/scoped_signal_test.c new file mode 100644 index 000000000000..8df027e22324 --- /dev/null +++ b/tools/testing/selftests/landlock/scoped_signal_test.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Landlock tests - Signal Scoping + * + * Copyright © 2024 Tahera Fahimi <fahimitahera@...il.com> + */ + +#define _GNU_SOURCE +#include <errno.h> +#include <fcntl.h> +#include <linux/landlock.h> +#include <signal.h> +#include <sys/prctl.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "common.h" +#include "scoped_common.h" + +/* This variable is used for handeling several signals. */ +static volatile sig_atomic_t is_signaled; + +/* clang-format off */ +FIXTURE(scoping_signals) {}; +/* clang-format on */ + +FIXTURE_VARIANT(scoping_signals) +{ + int sig; +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(scoping_signals, sigtrap) { + /* clang-format on */ + .sig = SIGTRAP, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(scoping_signals, sigurg) { + /* clang-format on */ + .sig = SIGURG, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(scoping_signals, sighup) { + /* clang-format on */ + .sig = SIGHUP, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(scoping_signals, sigtstp) { + /* clang-format on */ + .sig = SIGTSTP, +}; + +FIXTURE_SETUP(scoping_signals) +{ + is_signaled = 0; +} + +FIXTURE_TEARDOWN(scoping_signals) +{ +} + +static void scope_signal_handler(int sig, siginfo_t *info, void *ucontext) +{ + if (sig == SIGTRAP || sig == SIGURG || sig == SIGHUP || sig == SIGTSTP) + is_signaled = 1; +} + +/* + * In this test, a child process sends a signal to parent before and + * after getting scoped. + */ +TEST_F(scoping_signals, send_sig_to_parent) +{ + pid_t child; + pid_t parent = getpid(); + int status; + struct sigaction action = { + .sa_sigaction = scope_signal_handler, + .sa_flags = SA_SIGINFO, + + }; + + ASSERT_LE(0, sigaction(variant->sig, &action, NULL)); + + /* The process should not have already been signaled. */ + EXPECT_EQ(0, is_signaled); + + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + int err; + + /* + * The child process can send signal to parent when + * domain is not scoped. + */ + err = kill(parent, variant->sig); + ASSERT_EQ(0, err); + + create_scoped_domain(_metadata, LANDLOCK_SCOPED_SIGNAL); + + /* + * The child process cannot send signal to the parent + * anymore. + */ + err = kill(parent, variant->sig); + ASSERT_EQ(-1, err); + ASSERT_EQ(EPERM, errno); + + /* + * No matter of the domain, a process should be able to + * send a signal to itself. + */ + ASSERT_EQ(0, is_signaled); + ASSERT_EQ(0, raise(variant->sig)); + ASSERT_EQ(1, is_signaled); + + _exit(_metadata->exit_code); + return; + } + + while (!is_signaled && !usleep(1)) + ; + ASSERT_EQ(1, is_signaled); + + ASSERT_EQ(child, waitpid(child, &status, 0)); + + if (WIFSIGNALED(status) || !WIFEXITED(status) || + WEXITSTATUS(status) != EXIT_SUCCESS) + _metadata->exit_code = KSFT_FAIL; +} + +/* clang-format off */ +FIXTURE(scoped_domains) {}; +/* clang-format on */ + +#include "scoped_base_variants.h" + +FIXTURE_SETUP(scoped_domains) +{ +} + +FIXTURE_TEARDOWN(scoped_domains) +{ +} + +/* + * This test ensures that a scoped process cannot send signal out of + * scoped domain. + */ +TEST_F(scoped_domains, check_access_signal) +{ + pid_t child; + pid_t parent = getpid(); + int status; + bool can_signal_child, can_signal_parent; + int pipe_parent[2], pipe_child[2]; + int err; + char buf; + + can_signal_parent = !variant->domain_child; + can_signal_child = !variant->domain_parent; + + if (variant->domain_both) + create_scoped_domain(_metadata, LANDLOCK_SCOPED_SIGNAL); + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); + ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); + + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + ASSERT_EQ(0, close(pipe_child[0])); + ASSERT_EQ(0, close(pipe_parent[1])); + + if (variant->domain_child) + create_scoped_domain(_metadata, LANDLOCK_SCOPED_SIGNAL); + + ASSERT_EQ(1, write(pipe_child[1], ".", 1)); + + /* Waits for the parent to send signals. */ + ASSERT_EQ(1, read(pipe_parent[0], &buf, 1)); + + err = kill(parent, 0); + if (can_signal_parent) { + ASSERT_EQ(0, err); + } else { + ASSERT_EQ(-1, err); + ASSERT_EQ(EPERM, errno); + } + /* + * No matter of the domain, a process should be able to + * send a signal to itself. + */ + ASSERT_EQ(0, raise(0)); + + _exit(_metadata->exit_code); + return; + } + ASSERT_EQ(0, close(pipe_parent[0])); + if (variant->domain_parent) + create_scoped_domain(_metadata, LANDLOCK_SCOPED_SIGNAL); + + ASSERT_EQ(1, read(pipe_child[0], &buf, 1)); + + err = kill(child, 0); + if (can_signal_child) { + ASSERT_EQ(0, err); + } else { + ASSERT_EQ(-1, err); + ASSERT_EQ(EPERM, errno); + } + ASSERT_EQ(0, raise(0)); + + 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->exit_code = KSFT_FAIL; +} + +TEST_HARNESS_MAIN -- 2.34.1
Powered by blists - more mailing lists