lkml.org 
[lkml]   [2022]   [Jan]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.15 653/846] selftests/powerpc: Add a test of sigreturning to the kernel
    Date
    From: Michael Ellerman <mpe@ellerman.id.au>

    [ Upstream commit a8968521cfdc3e339fe69473d6632e0aa8d7202a ]

    We have a general signal fuzzer, sigfuz, which can modify the MSR & NIP
    before sigreturn. But the chance of it hitting a kernel address and also
    clearing MSR_PR is fairly slim.

    So add a specific test of sigreturn to a kernel address, both with and
    without attempting to clear MSR_PR (which the kernel must block).

    Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
    Link: https://lore.kernel.org/r/20211209115944.4062384-1-mpe@ellerman.id.au
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    .../selftests/powerpc/signal/.gitignore | 1 +
    .../testing/selftests/powerpc/signal/Makefile | 1 +
    .../powerpc/signal/sigreturn_kernel.c | 132 ++++++++++++++++++
    3 files changed, 134 insertions(+)
    create mode 100644 tools/testing/selftests/powerpc/signal/sigreturn_kernel.c

    diff --git a/tools/testing/selftests/powerpc/signal/.gitignore b/tools/testing/selftests/powerpc/signal/.gitignore
    index ce3375cd8e73e..8f6c816099a48 100644
    --- a/tools/testing/selftests/powerpc/signal/.gitignore
    +++ b/tools/testing/selftests/powerpc/signal/.gitignore
    @@ -4,3 +4,4 @@ signal_tm
    sigfuz
    sigreturn_vdso
    sig_sc_double_restart
    +sigreturn_kernel
    diff --git a/tools/testing/selftests/powerpc/signal/Makefile b/tools/testing/selftests/powerpc/signal/Makefile
    index d6ae54663aed7..84e201572466d 100644
    --- a/tools/testing/selftests/powerpc/signal/Makefile
    +++ b/tools/testing/selftests/powerpc/signal/Makefile
    @@ -1,5 +1,6 @@
    # SPDX-License-Identifier: GPL-2.0
    TEST_GEN_PROGS := signal signal_tm sigfuz sigreturn_vdso sig_sc_double_restart
    +TEST_GEN_PROGS += sigreturn_kernel

    CFLAGS += -maltivec
    $(OUTPUT)/signal_tm: CFLAGS += -mhtm
    diff --git a/tools/testing/selftests/powerpc/signal/sigreturn_kernel.c b/tools/testing/selftests/powerpc/signal/sigreturn_kernel.c
    new file mode 100644
    index 0000000000000..0a1b6e591eeed
    --- /dev/null
    +++ b/tools/testing/selftests/powerpc/signal/sigreturn_kernel.c
    @@ -0,0 +1,132 @@
    +// SPDX-License-Identifier: GPL-2.0
    +/*
    + * Test that we can't sigreturn to kernel addresses, or to kernel mode.
    + */
    +
    +#define _GNU_SOURCE
    +
    +#include <stdio.h>
    +#include <signal.h>
    +#include <stdlib.h>
    +#include <sys/types.h>
    +#include <sys/wait.h>
    +#include <unistd.h>
    +
    +#include "utils.h"
    +
    +#define MSR_PR (1ul << 14)
    +
    +static volatile unsigned long long sigreturn_addr;
    +static volatile unsigned long long sigreturn_msr_mask;
    +
    +static void sigusr1_handler(int signo, siginfo_t *si, void *uc_ptr)
    +{
    + ucontext_t *uc = (ucontext_t *)uc_ptr;
    +
    + if (sigreturn_addr)
    + UCONTEXT_NIA(uc) = sigreturn_addr;
    +
    + if (sigreturn_msr_mask)
    + UCONTEXT_MSR(uc) &= sigreturn_msr_mask;
    +}
    +
    +static pid_t fork_child(void)
    +{
    + pid_t pid;
    +
    + pid = fork();
    + if (pid == 0) {
    + raise(SIGUSR1);
    + exit(0);
    + }
    +
    + return pid;
    +}
    +
    +static int expect_segv(pid_t pid)
    +{
    + int child_ret;
    +
    + waitpid(pid, &child_ret, 0);
    + FAIL_IF(WIFEXITED(child_ret));
    + FAIL_IF(!WIFSIGNALED(child_ret));
    + FAIL_IF(WTERMSIG(child_ret) != 11);
    +
    + return 0;
    +}
    +
    +int test_sigreturn_kernel(void)
    +{
    + struct sigaction act;
    + int child_ret, i;
    + pid_t pid;
    +
    + act.sa_sigaction = sigusr1_handler;
    + act.sa_flags = SA_SIGINFO;
    + sigemptyset(&act.sa_mask);
    +
    + FAIL_IF(sigaction(SIGUSR1, &act, NULL));
    +
    + for (i = 0; i < 2; i++) {
    + // Return to kernel
    + sigreturn_addr = 0xcull << 60;
    + pid = fork_child();
    + expect_segv(pid);
    +
    + // Return to kernel virtual
    + sigreturn_addr = 0xc008ull << 48;
    + pid = fork_child();
    + expect_segv(pid);
    +
    + // Return out of range
    + sigreturn_addr = 0xc010ull << 48;
    + pid = fork_child();
    + expect_segv(pid);
    +
    + // Return to no-man's land, just below PAGE_OFFSET
    + sigreturn_addr = (0xcull << 60) - (64 * 1024);
    + pid = fork_child();
    + expect_segv(pid);
    +
    + // Return to no-man's land, above TASK_SIZE_4PB
    + sigreturn_addr = 0x1ull << 52;
    + pid = fork_child();
    + expect_segv(pid);
    +
    + // Return to 0xd space
    + sigreturn_addr = 0xdull << 60;
    + pid = fork_child();
    + expect_segv(pid);
    +
    + // Return to 0xe space
    + sigreturn_addr = 0xeull << 60;
    + pid = fork_child();
    + expect_segv(pid);
    +
    + // Return to 0xf space
    + sigreturn_addr = 0xfull << 60;
    + pid = fork_child();
    + expect_segv(pid);
    +
    + // Attempt to set PR=0 for 2nd loop (should be blocked by kernel)
    + sigreturn_msr_mask = ~MSR_PR;
    + }
    +
    + printf("All children killed as expected\n");
    +
    + // Don't change address, just MSR, should return to user as normal
    + sigreturn_addr = 0;
    + sigreturn_msr_mask = ~MSR_PR;
    + pid = fork_child();
    + waitpid(pid, &child_ret, 0);
    + FAIL_IF(!WIFEXITED(child_ret));
    + FAIL_IF(WIFSIGNALED(child_ret));
    + FAIL_IF(WEXITSTATUS(child_ret) != 0);
    +
    + return 0;
    +}
    +
    +int main(void)
    +{
    + return test_harness(test_sigreturn_kernel, "sigreturn_kernel");
    +}
    --
    2.34.1


    \
     
     \ /
      Last update: 2022-01-24 22:41    [W:4.030 / U:0.424 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site