linux/tools/testing/selftests/powerpc/signal/sigreturn_kernel.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Test that we can't sigreturn to kernel addresses, or to kernel mode.
   4 */
   5
   6#define _GNU_SOURCE
   7
   8#include <stdio.h>
   9#include <signal.h>
  10#include <stdlib.h>
  11#include <sys/types.h>
  12#include <sys/wait.h>
  13#include <unistd.h>
  14
  15#include "utils.h"
  16
  17#define MSR_PR (1ul << 14)
  18
  19static volatile unsigned long long sigreturn_addr;
  20static volatile unsigned long long sigreturn_msr_mask;
  21
  22static void sigusr1_handler(int signo, siginfo_t *si, void *uc_ptr)
  23{
  24        ucontext_t *uc = (ucontext_t *)uc_ptr;
  25
  26        if (sigreturn_addr)
  27                UCONTEXT_NIA(uc) = sigreturn_addr;
  28
  29        if (sigreturn_msr_mask)
  30                UCONTEXT_MSR(uc) &= sigreturn_msr_mask;
  31}
  32
  33static pid_t fork_child(void)
  34{
  35        pid_t pid;
  36
  37        pid = fork();
  38        if (pid == 0) {
  39                raise(SIGUSR1);
  40                exit(0);
  41        }
  42
  43        return pid;
  44}
  45
  46static int expect_segv(pid_t pid)
  47{
  48        int child_ret;
  49
  50        waitpid(pid, &child_ret, 0);
  51        FAIL_IF(WIFEXITED(child_ret));
  52        FAIL_IF(!WIFSIGNALED(child_ret));
  53        FAIL_IF(WTERMSIG(child_ret) != 11);
  54
  55        return 0;
  56}
  57
  58int test_sigreturn_kernel(void)
  59{
  60        struct sigaction act;
  61        int child_ret, i;
  62        pid_t pid;
  63
  64        act.sa_sigaction = sigusr1_handler;
  65        act.sa_flags = SA_SIGINFO;
  66        sigemptyset(&act.sa_mask);
  67
  68        FAIL_IF(sigaction(SIGUSR1, &act, NULL));
  69
  70        for (i = 0; i < 2; i++) {
  71                // Return to kernel
  72                sigreturn_addr = 0xcull << 60;
  73                pid = fork_child();
  74                expect_segv(pid);
  75
  76                // Return to kernel virtual
  77                sigreturn_addr = 0xc008ull << 48;
  78                pid = fork_child();
  79                expect_segv(pid);
  80
  81                // Return out of range
  82                sigreturn_addr = 0xc010ull << 48;
  83                pid = fork_child();
  84                expect_segv(pid);
  85
  86                // Return to no-man's land, just below PAGE_OFFSET
  87                sigreturn_addr = (0xcull << 60) - (64 * 1024);
  88                pid = fork_child();
  89                expect_segv(pid);
  90
  91                // Return to no-man's land, above TASK_SIZE_4PB
  92                sigreturn_addr = 0x1ull << 52;
  93                pid = fork_child();
  94                expect_segv(pid);
  95
  96                // Return to 0xd space
  97                sigreturn_addr = 0xdull << 60;
  98                pid = fork_child();
  99                expect_segv(pid);
 100
 101                // Return to 0xe space
 102                sigreturn_addr = 0xeull << 60;
 103                pid = fork_child();
 104                expect_segv(pid);
 105
 106                // Return to 0xf space
 107                sigreturn_addr = 0xfull << 60;
 108                pid = fork_child();
 109                expect_segv(pid);
 110
 111                // Attempt to set PR=0 for 2nd loop (should be blocked by kernel)
 112                sigreturn_msr_mask = ~MSR_PR;
 113        }
 114
 115        printf("All children killed as expected\n");
 116
 117        // Don't change address, just MSR, should return to user as normal
 118        sigreturn_addr = 0;
 119        sigreturn_msr_mask = ~MSR_PR;
 120        pid = fork_child();
 121        waitpid(pid, &child_ret, 0);
 122        FAIL_IF(!WIFEXITED(child_ret));
 123        FAIL_IF(WIFSIGNALED(child_ret));
 124        FAIL_IF(WEXITSTATUS(child_ret) != 0);
 125
 126        return 0;
 127}
 128
 129int main(void)
 130{
 131        return test_harness(test_sigreturn_kernel, "sigreturn_kernel");
 132}
 133