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:
Michael Ellerman 2020-03-04 22:04:02 +11:00
parent 59ed2adf39
commit a0968a025c
3 changed files with 129 additions and 1 deletions

View File

@ -1,3 +1,4 @@
signal
signal_tm
sigfuz
sigreturn_vdso

View File

@ -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

View File

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