mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 21:51:40 +00:00
selftests/landlock: Test signal created by out-of-bound message
Add a test to verify that the SIGURG signal created by an out-of-bound message in UNIX sockets is well controlled by the file_send_sigiotask hook. Test coverage for security/landlock is 92.2% of 1046 lines according to gcc/gcov-14. Signed-off-by: Tahera Fahimi <fahimitahera@gmail.com> Link: https://lore.kernel.org/r/50daeed4d4f60d71e9564d0f24004a373fc5f7d5.1725657728.git.fahimitahera@gmail.com [mic: Improve commit message and add test coverage, improve test with four variants to fully cover the hook, use abstract unix socket to avoid managing a file, use dedicated variable per process, add comments, avoid negative ASSERT, move close calls] Co-developed-by: Mickaël Salaün <mic@digikod.net> Signed-off-by: Mickaël Salaün <mic@digikod.net>
This commit is contained in:
parent
c899496501
commit
f34e9ce5f4
@ -297,4 +297,188 @@ TEST(signal_scoping_threads)
|
||||
EXPECT_EQ(0, close(thread_pipe[1]));
|
||||
}
|
||||
|
||||
const short backlog = 10;
|
||||
|
||||
static volatile sig_atomic_t signal_received;
|
||||
|
||||
static void handle_sigurg(int sig)
|
||||
{
|
||||
if (sig == SIGURG)
|
||||
signal_received = 1;
|
||||
else
|
||||
signal_received = -1;
|
||||
}
|
||||
|
||||
static int setup_signal_handler(int signal)
|
||||
{
|
||||
struct sigaction sa = {
|
||||
.sa_handler = handle_sigurg,
|
||||
};
|
||||
|
||||
if (sigemptyset(&sa.sa_mask))
|
||||
return -1;
|
||||
|
||||
sa.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
return sigaction(SIGURG, &sa, NULL);
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
FIXTURE(fown) {};
|
||||
/* clang-format on */
|
||||
|
||||
enum fown_sandbox {
|
||||
SANDBOX_NONE,
|
||||
SANDBOX_BEFORE_FORK,
|
||||
SANDBOX_BEFORE_SETOWN,
|
||||
SANDBOX_AFTER_SETOWN,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT(fown)
|
||||
{
|
||||
const enum fown_sandbox sandbox_setown;
|
||||
};
|
||||
|
||||
/* clang-format off */
|
||||
FIXTURE_VARIANT_ADD(fown, no_sandbox) {
|
||||
/* clang-format on */
|
||||
.sandbox_setown = SANDBOX_NONE,
|
||||
};
|
||||
|
||||
/* clang-format off */
|
||||
FIXTURE_VARIANT_ADD(fown, sandbox_before_fork) {
|
||||
/* clang-format on */
|
||||
.sandbox_setown = SANDBOX_BEFORE_FORK,
|
||||
};
|
||||
|
||||
/* clang-format off */
|
||||
FIXTURE_VARIANT_ADD(fown, sandbox_before_setown) {
|
||||
/* clang-format on */
|
||||
.sandbox_setown = SANDBOX_BEFORE_SETOWN,
|
||||
};
|
||||
|
||||
/* clang-format off */
|
||||
FIXTURE_VARIANT_ADD(fown, sandbox_after_setown) {
|
||||
/* clang-format on */
|
||||
.sandbox_setown = SANDBOX_AFTER_SETOWN,
|
||||
};
|
||||
|
||||
FIXTURE_SETUP(fown)
|
||||
{
|
||||
drop_caps(_metadata);
|
||||
}
|
||||
|
||||
FIXTURE_TEARDOWN(fown)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Sending an out of bound message will trigger the SIGURG signal
|
||||
* through file_send_sigiotask.
|
||||
*/
|
||||
TEST_F(fown, sigurg_socket)
|
||||
{
|
||||
int server_socket, recv_socket;
|
||||
struct service_fixture server_address;
|
||||
char buffer_parent;
|
||||
int status;
|
||||
int pipe_parent[2], pipe_child[2];
|
||||
pid_t child;
|
||||
|
||||
memset(&server_address, 0, sizeof(server_address));
|
||||
set_unix_address(&server_address, 0);
|
||||
|
||||
ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
|
||||
ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
|
||||
|
||||
if (variant->sandbox_setown == SANDBOX_BEFORE_FORK)
|
||||
create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
|
||||
|
||||
child = fork();
|
||||
ASSERT_LE(0, child);
|
||||
if (child == 0) {
|
||||
int client_socket;
|
||||
char buffer_child;
|
||||
|
||||
EXPECT_EQ(0, close(pipe_parent[1]));
|
||||
EXPECT_EQ(0, close(pipe_child[0]));
|
||||
|
||||
ASSERT_EQ(0, setup_signal_handler(SIGURG));
|
||||
client_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
ASSERT_LE(0, client_socket);
|
||||
|
||||
/* Waits for the parent to listen. */
|
||||
ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1));
|
||||
ASSERT_EQ(0, connect(client_socket, &server_address.unix_addr,
|
||||
server_address.unix_addr_len));
|
||||
|
||||
/*
|
||||
* Waits for the parent to accept the connection, sandbox
|
||||
* itself, and call fcntl(2).
|
||||
*/
|
||||
ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1));
|
||||
/* May signal itself. */
|
||||
ASSERT_EQ(1, send(client_socket, ".", 1, MSG_OOB));
|
||||
EXPECT_EQ(0, close(client_socket));
|
||||
ASSERT_EQ(1, write(pipe_child[1], ".", 1));
|
||||
EXPECT_EQ(0, close(pipe_child[1]));
|
||||
|
||||
/* Waits for the message to be received. */
|
||||
ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1));
|
||||
EXPECT_EQ(0, close(pipe_parent[0]));
|
||||
|
||||
if (variant->sandbox_setown == SANDBOX_BEFORE_SETOWN) {
|
||||
ASSERT_EQ(0, signal_received);
|
||||
} else {
|
||||
/*
|
||||
* A signal is only received if fcntl(F_SETOWN) was
|
||||
* called before any sandboxing or if the signal
|
||||
* receiver is in the same domain.
|
||||
*/
|
||||
ASSERT_EQ(1, signal_received);
|
||||
}
|
||||
_exit(_metadata->exit_code);
|
||||
return;
|
||||
}
|
||||
EXPECT_EQ(0, close(pipe_parent[0]));
|
||||
EXPECT_EQ(0, close(pipe_child[1]));
|
||||
|
||||
server_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
ASSERT_LE(0, server_socket);
|
||||
ASSERT_EQ(0, bind(server_socket, &server_address.unix_addr,
|
||||
server_address.unix_addr_len));
|
||||
ASSERT_EQ(0, listen(server_socket, backlog));
|
||||
ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
|
||||
|
||||
recv_socket = accept(server_socket, NULL, NULL);
|
||||
ASSERT_LE(0, recv_socket);
|
||||
|
||||
if (variant->sandbox_setown == SANDBOX_BEFORE_SETOWN)
|
||||
create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
|
||||
|
||||
/*
|
||||
* Sets the child to receive SIGURG for MSG_OOB. This uncommon use is
|
||||
* a valid attack scenario which also simplifies this test.
|
||||
*/
|
||||
ASSERT_EQ(0, fcntl(recv_socket, F_SETOWN, child));
|
||||
|
||||
if (variant->sandbox_setown == SANDBOX_AFTER_SETOWN)
|
||||
create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
|
||||
|
||||
ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
|
||||
|
||||
/* Waits for the child to send MSG_OOB. */
|
||||
ASSERT_EQ(1, read(pipe_child[0], &buffer_parent, 1));
|
||||
EXPECT_EQ(0, close(pipe_child[0]));
|
||||
ASSERT_EQ(1, recv(recv_socket, &buffer_parent, 1, MSG_OOB));
|
||||
EXPECT_EQ(0, close(recv_socket));
|
||||
EXPECT_EQ(0, close(server_socket));
|
||||
ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
|
||||
EXPECT_EQ(0, close(pipe_parent[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
|
||||
|
Loading…
Reference in New Issue
Block a user