forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
selftests/powerpc: Add a test of sigreturning to the kernel
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 <[email protected]> Link: https://lore.kernel.org/r/[email protected]
- Loading branch information
Showing
3 changed files
with
134 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,3 +4,4 @@ signal_tm | |
sigfuz | ||
sigreturn_vdso | ||
sig_sc_double_restart | ||
sigreturn_kernel |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
132 changes: 132 additions & 0 deletions
132
tools/testing/selftests/powerpc/signal/sigreturn_kernel.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"); | ||
} |