Commit a0968a02 authored by Michael Ellerman's avatar Michael Ellerman

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: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200304110402.6038-1-mpe@ellerman.id.au
parent 59ed2adf
# 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
......
// 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");
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment