-
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 sigreturn vs VDSO
There's two different paths through the sigreturn code, depending on whether the VDSO is mapped or not. We recently discovered a bug in the unmapped case, because it's not commonly used these days. So add a test that sends itself a signal, then moves the VDSO, takes another signal and finally unmaps the VDSO before sending itself another signal. That tests the standard signal path, the code that handles the VDSO being moved, and also the signal path in the case where the VDSO is unmapped. Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20200304110402.6038-1-mpe@ellerman.id.au
- Loading branch information
Michael Ellerman
committed
Mar 20, 2020
1 parent
59ed2ad
commit a0968a0
Showing
3 changed files
with
129 additions
and
1 deletion.
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 |
---|---|---|
@@ -1,3 +1,4 @@ | ||
signal | ||
signal_tm | ||
sigfuz | ||
sigreturn_vdso |
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
127 changes: 127 additions & 0 deletions
127
tools/testing/selftests/powerpc/signal/sigreturn_vdso.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,127 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* | ||
* Test that we can take signals with and without the VDSO mapped, which trigger | ||
* different paths in the signal handling code. | ||
* | ||
* See handle_rt_signal64() and setup_trampoline() in signal_64.c | ||
*/ | ||
|
||
#define _GNU_SOURCE | ||
|
||
#include <errno.h> | ||
#include <stdio.h> | ||
#include <signal.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <sys/mman.h> | ||
#include <sys/types.h> | ||
#include <unistd.h> | ||
|
||
// Ensure assert() is not compiled out | ||
#undef NDEBUG | ||
#include <assert.h> | ||
|
||
#include "utils.h" | ||
|
||
static int search_proc_maps(char *needle, unsigned long *low, unsigned long *high) | ||
{ | ||
unsigned long start, end; | ||
static char buf[4096]; | ||
char name[128]; | ||
FILE *f; | ||
int rc = -1; | ||
|
||
f = fopen("/proc/self/maps", "r"); | ||
if (!f) { | ||
perror("fopen"); | ||
return -1; | ||
} | ||
|
||
while (fgets(buf, sizeof(buf), f)) { | ||
rc = sscanf(buf, "%lx-%lx %*c%*c%*c%*c %*x %*d:%*d %*d %127s\n", | ||
&start, &end, name); | ||
if (rc == 2) | ||
continue; | ||
|
||
if (rc != 3) { | ||
printf("sscanf errored\n"); | ||
rc = -1; | ||
break; | ||
} | ||
|
||
if (strstr(name, needle)) { | ||
*low = start; | ||
*high = end - 1; | ||
rc = 0; | ||
break; | ||
} | ||
} | ||
|
||
fclose(f); | ||
|
||
return rc; | ||
} | ||
|
||
static volatile sig_atomic_t took_signal = 0; | ||
|
||
static void sigusr1_handler(int sig) | ||
{ | ||
took_signal++; | ||
} | ||
|
||
int test_sigreturn_vdso(void) | ||
{ | ||
unsigned long low, high, size; | ||
struct sigaction act; | ||
char *p; | ||
|
||
act.sa_handler = sigusr1_handler; | ||
act.sa_flags = 0; | ||
sigemptyset(&act.sa_mask); | ||
|
||
assert(sigaction(SIGUSR1, &act, NULL) == 0); | ||
|
||
// Confirm the VDSO is mapped, and work out where it is | ||
assert(search_proc_maps("[vdso]", &low, &high) == 0); | ||
size = high - low + 1; | ||
printf("VDSO is at 0x%lx-0x%lx (%lu bytes)\n", low, high, size); | ||
|
||
kill(getpid(), SIGUSR1); | ||
assert(took_signal == 1); | ||
printf("Signal delivered OK with VDSO mapped\n"); | ||
|
||
// Remap the VDSO somewhere else | ||
p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); | ||
assert(p != MAP_FAILED); | ||
assert(mremap((void *)low, size, size, MREMAP_MAYMOVE|MREMAP_FIXED, p) != MAP_FAILED); | ||
assert(search_proc_maps("[vdso]", &low, &high) == 0); | ||
size = high - low + 1; | ||
printf("VDSO moved to 0x%lx-0x%lx (%lu bytes)\n", low, high, size); | ||
|
||
kill(getpid(), SIGUSR1); | ||
assert(took_signal == 2); | ||
printf("Signal delivered OK with VDSO moved\n"); | ||
|
||
assert(munmap((void *)low, size) == 0); | ||
printf("Unmapped VDSO\n"); | ||
|
||
// Confirm the VDSO is not mapped anymore | ||
assert(search_proc_maps("[vdso]", &low, &high) != 0); | ||
|
||
// Make the stack executable | ||
assert(search_proc_maps("[stack]", &low, &high) == 0); | ||
size = high - low + 1; | ||
mprotect((void *)low, size, PROT_READ|PROT_WRITE|PROT_EXEC); | ||
printf("Remapped the stack executable\n"); | ||
|
||
kill(getpid(), SIGUSR1); | ||
assert(took_signal == 3); | ||
printf("Signal delivered OK with VDSO unmapped\n"); | ||
|
||
return 0; | ||
} | ||
|
||
int main(void) | ||
{ | ||
return test_harness(test_sigreturn_vdso, "sigreturn_vdso"); | ||
} |