Skip to content

Commit

Permalink
selftests/powerpc: Add a test of sigreturn vs VDSO
Browse files Browse the repository at this point in the history
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
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 1 deletion.
1 change: 1 addition & 0 deletions tools/testing/selftests/powerpc/signal/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
signal
signal_tm
sigfuz
sigreturn_vdso
2 changes: 1 addition & 1 deletion tools/testing/selftests/powerpc/signal/Makefile
Original file line number Diff line number Diff line change
@@ -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
Expand Down
127 changes: 127 additions & 0 deletions tools/testing/selftests/powerpc/signal/sigreturn_vdso.c
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");
}

0 comments on commit a0968a0

Please sign in to comment.