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
This commit is contained in:
parent
59ed2adf39
commit
a0968a025c
|
@ -1,3 +1,4 @@
|
|||
signal
|
||||
signal_tm
|
||||
sigfuz
|
||||
sigreturn_vdso
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
TEST_GEN_PROGS := signal signal_tm sigfuz
|
||||
TEST_GEN_PROGS := signal signal_tm sigfuz sigreturn_vdso
|
||||
|
||||
CFLAGS += -maltivec
|
||||
$(OUTPUT)/signal_tm: CFLAGS += -mhtm
|
||||
|
|
|
@ -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");
|
||||
}
|
Loading…
Reference in New Issue