From: "Mickaël Salaün" <mic@digikod.net>
To: Tahera Fahimi <fahimitahera@gmail.com>
Cc: outreachy@lists.linux.dev, gnoack@google.com,
paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com,
linux-security-module@vger.kernel.org,
linux-kernel@vger.kernel.org, bjorn3_gh@protonmail.com,
jannh@google.com, netdev@vger.kernel.org
Subject: Re: [PATCH v3 3/6] selftest/Landlock: Signal restriction tests
Date: Mon, 26 Aug 2024 14:40:07 +0200 [thread overview]
Message-ID: <20240826.queS8Fah5foo@digikod.net> (raw)
In-Reply-To: <82b0d2103013397d8b46de9fc7c8c78456055299.1723680305.git.fahimitahera@gmail.com>
On Thu, Aug 15, 2024 at 12:29:22PM -0600, Tahera Fahimi wrote:
> 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.
>
> Signed-off-by: Tahera Fahimi <fahimitahera@gmail.com>
> ---
> Chnages in versions:
> 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 | 302 ++++++++++++++++++
> 1 file changed, 302 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..92958c6266ca
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/scoped_signal_test.c
> @@ -0,0 +1,302 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Landlock tests - Signal Scoping
> + *
> + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
> + * Copyright © 2019-2020 ANSSI
> + */
> +
> +#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"
> +
> +static sig_atomic_t signaled;
static volatile sig_atomic_t signaled;
> +
> +static void create_signal_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 || sig == SIGUSR1) {
> + signaled = 1;
> + }
> +}
> +
> +/* 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;
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(signal_scoping, allow_without_domain) {
> + /* clang-format on */
> + .sig = 0,
> + .domain_both = false,
> + .domain_parent = false,
> + .domain_child = false,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(signal_scoping, deny_with_child_domain) {
> + /* clang-format on */
> + .sig = 0,
> + .domain_both = false,
> + .domain_parent = false,
> + .domain_child = true,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(signal_scoping, allow_with_parent_domain) {
> + /* clang-format on */
> + .sig = 0,
> + .domain_both = false,
> + .domain_parent = true,
> + .domain_child = false,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(signal_scoping, deny_with_sibling_domain) {
> + /* clang-format on */
> + .sig = 0,
> + .domain_both = false,
> + .domain_parent = true,
> + .domain_child = true,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(signal_scoping, allow_sibling_domain) {
> + /* clang-format on */
> + .sig = 0,
> + .domain_both = true,
> + .domain_parent = false,
> + .domain_child = false,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(signal_scoping, deny_with_nested_domain) {
> + /* clang-format on */
> + .sig = 0,
> + .domain_both = true,
> + .domain_parent = false,
> + .domain_child = true,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(signal_scoping, allow_with_nested_and_parent_domain) {
> + /* clang-format on */
> + .sig = 0,
> + .domain_both = true,
> + .domain_parent = true,
> + .domain_child = false,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(signal_scoping, deny_with_forked_domain) {
> + /* clang-format on */
> + .sig = 0,
> + .domain_both = true,
> + .domain_parent = true,
> + .domain_child = true,
> +};
> +
> +/* Default Action: Terminate*/
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(signal_scoping, deny_with_forked_domain_SIGHUP) {
> + /* 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_SIGHUP) {
> + /* 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,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(signal_scoping, deny_with_forked_domain_SIGUSR1) {
> + /* clang-format on */
> + .sig = SIGUSR1,
> + .domain_both = true,
> + .domain_parent = true,
> + .domain_child = true,
> +};
> +
> +FIXTURE_SETUP(signal_scoping)
> +{
> +}
> +
> +FIXTURE_TEARDOWN(signal_scoping)
> +{
> +}
> +
> +TEST_F(signal_scoping, test_signal)
Sometime, this test hang. I suspect the following issue:
> +{
> + pid_t child;
> + pid_t parent = getpid();
> + int status;
> + bool can_signal;
> + int pipe_parent[2];
> + struct sigaction action = {
> + .sa_sigaction = scope_signal_handler,
> + .sa_flags = SA_SIGINFO,
> +
> + };
> +
> + can_signal = !variant->domain_child;
> +
> + if (variant->sig > 0)
> + ASSERT_LE(0, sigaction(variant->sig, &action, NULL));
> +
> + if (variant->domain_both) {
> + create_signal_domain(_metadata);
> + if (!__test_passed(_metadata))
> + /* Aborts before forking. */
> + return;
> + }
> + 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]));
> + if (variant->domain_child)
> + create_signal_domain(_metadata);
> +
> + /* Waits for the parent to be in a domain, if any. */
> + ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
There is a race condition here when the parent process didn't yet called
pause().
> +
> + err = kill(parent, variant->sig);
> + if (can_signal) {
> + 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(variant->sig));
> + if (variant->sig > 0)
> + ASSERT_EQ(1, signaled);
> + _exit(_metadata->exit_code);
> + return;
> + }
> + ASSERT_EQ(0, close(pipe_parent[0]));
> + if (variant->domain_parent)
> + create_signal_domain(_metadata);
> +
/* The process should not have already been signaled. */
EXPECT_EQ(0, signaled);
> + /* Signals that the parent is in a domain, if any. */
> + ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
> +
> + if (can_signal && variant->sig > 0) {
> + ASSERT_EQ(-1, pause());
> + ASSERT_EQ(EINTR, errno);
This can hang indefinitely if the child process sent the signal after
reading from the pipe and before the call to pause().
This should be a better alternative to the use of pause():
/* Avoids race condition with the child's signal. */
while (!signaled && !usleep(1));
ASSERT_EQ(1, signaled);
BTW, we cannot reliably check for errno because usleep() may still
return 0, but that's OK.
> + ASSERT_EQ(1, signaled);
> + } else {
> + ASSERT_EQ(0, signaled);
> + }
> +
> + 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
>
>
next prev parent reply other threads:[~2024-08-26 12:40 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-08-15 18:29 [PATCH v3 0/6] Landlock: Signal Scoping Support Tahera Fahimi
2024-08-15 18:29 ` [PATCH v3 1/6] Landlock: Add signal control Tahera Fahimi
2024-08-15 18:29 ` [PATCH v3 2/6] Landlock: Adding file_send_sigiotask signal scoping support Tahera Fahimi
2024-08-15 20:25 ` Jann Horn
2024-08-15 21:28 ` Tahera Fahimi
2024-08-15 22:10 ` Jann Horn
2024-08-15 23:06 ` Tahera Fahimi
2024-08-19 17:57 ` Mickaël Salaün
2024-08-21 10:13 ` Mickaël Salaün
2024-08-15 18:29 ` [PATCH v3 3/6] selftest/Landlock: Signal restriction tests Tahera Fahimi
2024-08-20 15:57 ` Mickaël Salaün
2024-08-26 12:40 ` Mickaël Salaün [this message]
2024-08-15 18:29 ` [PATCH v3 4/6] selftest/Landlock: pthread_kill(3) tests Tahera Fahimi
2024-08-20 15:57 ` Mickaël Salaün
2024-08-26 12:40 ` Mickaël Salaün
2024-08-15 18:29 ` [PATCH v3 5/6] sample/Landlock: Support signal scoping restriction Tahera Fahimi
2024-08-15 18:29 ` [PATCH v3 6/6] Landlock: Document LANDLOCK_SCOPED_SIGNAL Tahera Fahimi
2024-08-15 21:07 ` Tahera Fahimi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240826.queS8Fah5foo@digikod.net \
--to=mic@digikod.net \
--cc=bjorn3_gh@protonmail.com \
--cc=fahimitahera@gmail.com \
--cc=gnoack@google.com \
--cc=jannh@google.com \
--cc=jmorris@namei.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=outreachy@lists.linux.dev \
--cc=paul@paul-moore.com \
--cc=serge@hallyn.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.