// 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"); }