Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
signal
signal_tm
sigfuz
sigreturn_vdso
sig_sc_double_restart
sigreturn_kernel
sigreturn_unaligned
@@ -0,0 +1,17 @@
# SPDX-License-Identifier: GPL-2.0
TEST_GEN_PROGS := signal signal_tm sigfuz sigreturn_vdso sig_sc_double_restart
TEST_GEN_PROGS += sigreturn_kernel
TEST_GEN_PROGS += sigreturn_unaligned
$(OUTPUT)/signal_tm: CFLAGS += -mhtm
$(OUTPUT)/sigfuz: CFLAGS += -pthread -m64
TEST_FILES := settings
top_srcdir = ../../../../..
include ../../lib.mk
include ../flags.mk
CFLAGS += -maltivec
$(TEST_GEN_PROGS): ../harness.c ../utils.c signal.S
@@ -0,0 +1 @@
timeout=0
@@ -0,0 +1,174 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Test that a syscall does not get restarted twice, handled by trap_norestart()
*
* Based on Al's description, and a test for the bug fixed in this commit:
*
* commit 9a81c16b527528ad307843be5571111aa8d35a80
* Author: Al Viro <viro@zeniv.linux.org.uk>
* Date: Mon Sep 20 21:48:57 2010 +0100
*
* powerpc: fix double syscall restarts
*
* Make sigreturn zero regs->trap, make do_signal() do the same on all
* paths. As it is, signal interrupting e.g. read() from fd 512 (==
* ERESTARTSYS) with another signal getting unblocked when the first
* handler finishes will lead to restart one insn earlier than it ought
* to. Same for multiple signals with in-kernel handlers interrupting
* that sucker at the same time. Same for multiple signals of any kind
* interrupting that sucker on 64bit...
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "utils.h"
static void SIGUSR1_handler(int sig)
{
kill(getpid(), SIGUSR2);
/*
* SIGUSR2 is blocked until the handler exits, at which point it will
* be raised again and think there is a restart to be done because the
* pending restarted syscall has 512 (ERESTARTSYS) in r3. The second
* restart will retreat NIP another 4 bytes to fail case branch.
*/
}
static void SIGUSR2_handler(int sig)
{
}
static ssize_t raw_read(int fd, void *buf, size_t count)
{
register long nr asm("r0") = __NR_read;
register long _fd asm("r3") = fd;
register void *_buf asm("r4") = buf;
register size_t _count asm("r5") = count;
asm volatile(
" b 0f \n"
" b 1f \n"
" 0: sc 0 \n"
" bns 2f \n"
" neg %0,%0 \n"
" b 2f \n"
" 1: \n"
" li %0,%4 \n"
" 2: \n"
: "+r"(_fd), "+r"(nr), "+r"(_buf), "+r"(_count)
: "i"(-ENOANO)
: "memory", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "ctr", "cr0");
if (_fd < 0) {
errno = -_fd;
_fd = -1;
}
return _fd;
}
#define DATA "test 123"
#define DLEN (strlen(DATA)+1)
int test_restart(void)
{
int pipefd[2];
pid_t pid;
char buf[512];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) { /* Child reads from pipe */
struct sigaction act;
int fd;
memset(&act, 0, sizeof(act));
sigaddset(&act.sa_mask, SIGUSR2);
act.sa_handler = SIGUSR1_handler;
act.sa_flags = SA_RESTART;
if (sigaction(SIGUSR1, &act, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}
memset(&act, 0, sizeof(act));
act.sa_handler = SIGUSR2_handler;
act.sa_flags = SA_RESTART;
if (sigaction(SIGUSR2, &act, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}
/* Let's get ERESTARTSYS into r3 */
while ((fd = dup(pipefd[0])) != 512) {
if (fd == -1) {
perror("dup");
exit(EXIT_FAILURE);
}
}
if (raw_read(fd, buf, 512) == -1) {
if (errno == ENOANO) {
fprintf(stderr, "Double restart moved restart before sc instruction.\n");
_exit(EXIT_FAILURE);
}
perror("read");
exit(EXIT_FAILURE);
}
if (strncmp(buf, DATA, DLEN)) {
fprintf(stderr, "bad test string %s\n", buf);
exit(EXIT_FAILURE);
}
return 0;
} else {
int wstatus;
usleep(100000); /* Hack to get reader waiting */
kill(pid, SIGUSR1);
usleep(100000);
if (write(pipefd[1], DATA, DLEN) != DLEN) {
perror("write");
exit(EXIT_FAILURE);
}
close(pipefd[0]);
close(pipefd[1]);
if (wait(&wstatus) == -1) {
perror("wait");
exit(EXIT_FAILURE);
}
if (!WIFEXITED(wstatus)) {
fprintf(stderr, "child exited abnormally\n");
exit(EXIT_FAILURE);
}
FAIL_IF(WEXITSTATUS(wstatus) != EXIT_SUCCESS);
return 0;
}
}
int main(void)
{
test_harness_set_timeout(10);
return test_harness(test_restart, "sig sys restart");
}
@@ -0,0 +1,325 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2018, Breno Leitao, IBM Corp.
* Licensed under GPLv2.
*
* Sigfuz(tm): A PowerPC TM-aware signal fuzzer.
*
* This is a new selftest that raises SIGUSR1 signals and handles it in a set
* of different ways, trying to create different scenario for testing
* purpose.
*
* This test works raising a signal and calling sigreturn interleaved with
* TM operations, as starting, suspending and terminating a transaction. The
* test depends on random numbers, and, based on them, it sets different TM
* states.
*
* Other than that, the test fills out the user context struct that is passed
* to the sigreturn system call with random data, in order to make sure that
* the signal handler syscall can handle different and invalid states
* properly.
*
* This selftest has command line parameters to control what kind of tests the
* user wants to run, as for example, if a transaction should be started prior
* to signal being raised, or, after the signal being raised and before the
* sigreturn. If no parameter is given, the default is enabling all options.
*
* This test does not check if the user context is being read and set
* properly by the kernel. Its purpose, at this time, is basically
* guaranteeing that the kernel does not crash on invalid scenarios.
*/
#include <stdio.h>
#include <limits.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <ucontext.h>
#include <sys/mman.h>
#include <pthread.h>
#include "utils.h"
/* Selftest defaults */
#define COUNT_MAX 600 /* Number of interactions */
#define THREADS 16 /* Number of threads */
/* Arguments options */
#define ARG_MESS_WITH_TM_AT 0x1
#define ARG_MESS_WITH_TM_BEFORE 0x2
#define ARG_MESS_WITH_MSR_AT 0x4
#define ARG_FOREVER 0x10
#define ARG_COMPLETE (ARG_MESS_WITH_TM_AT | \
ARG_MESS_WITH_TM_BEFORE | \
ARG_MESS_WITH_MSR_AT)
static int args;
static int nthread = THREADS;
static int count_max = COUNT_MAX;
/* checkpoint context */
static ucontext_t *tmp_uc;
/* Return true with 1/x probability */
static int one_in_chance(int x)
{
return rand() % x == 0;
}
/* Change TM states */
static void mess_with_tm(void)
{
/* Starts a transaction 33% of the time */
if (one_in_chance(3)) {
asm ("tbegin. ;"
"beq 8 ;");
/* And suspended half of them */
if (one_in_chance(2))
asm("tsuspend. ;");
}
/* Call 'tend' in 5% of the runs */
if (one_in_chance(20))
asm("tend. ;");
}
/* Signal handler that will be invoked with raise() */
static void trap_signal_handler(int signo, siginfo_t *si, void *uc)
{
ucontext_t *ucp = uc;
ucp->uc_link = tmp_uc;
/*
* Set uc_link in three possible ways:
* - Setting a single 'int' in the whole chunk
* - Cloning ucp into uc_link
* - Allocating a new memory chunk
*/
if (one_in_chance(3)) {
memset(ucp->uc_link, rand(), sizeof(ucontext_t));
} else if (one_in_chance(2)) {
memcpy(ucp->uc_link, uc, sizeof(ucontext_t));
} else if (one_in_chance(2)) {
if (tmp_uc) {
free(tmp_uc);
tmp_uc = NULL;
}
tmp_uc = malloc(sizeof(ucontext_t));
ucp->uc_link = tmp_uc;
/* Trying to cause a major page fault at Kernel level */
madvise(ucp->uc_link, sizeof(ucontext_t), MADV_DONTNEED);
}
if (args & ARG_MESS_WITH_MSR_AT) {
/* Changing the checkpointed registers */
if (one_in_chance(4)) {
ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |= MSR_TS_S;
} else {
if (one_in_chance(2)) {
ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |=
MSR_TS_T;
} else if (one_in_chance(2)) {
ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |=
MSR_TS_T | MSR_TS_S;
}
}
/* Checking the current register context */
if (one_in_chance(2)) {
ucp->uc_mcontext.gp_regs[PT_MSR] |= MSR_TS_S;
} else if (one_in_chance(2)) {
if (one_in_chance(2))
ucp->uc_mcontext.gp_regs[PT_MSR] |=
MSR_TS_T;
else if (one_in_chance(2))
ucp->uc_mcontext.gp_regs[PT_MSR] |=
MSR_TS_T | MSR_TS_S;
}
}
if (one_in_chance(20)) {
/* Nested transaction start */
if (one_in_chance(5))
mess_with_tm();
/* Return without changing any other context info */
return;
}
if (one_in_chance(10))
ucp->uc_mcontext.gp_regs[PT_MSR] = random();
if (one_in_chance(10))
ucp->uc_mcontext.gp_regs[PT_NIP] = random();
if (one_in_chance(10))
ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] = random();
if (one_in_chance(10))
ucp->uc_link->uc_mcontext.gp_regs[PT_NIP] = random();
ucp->uc_mcontext.gp_regs[PT_TRAP] = random();
ucp->uc_mcontext.gp_regs[PT_DSISR] = random();
ucp->uc_mcontext.gp_regs[PT_DAR] = random();
ucp->uc_mcontext.gp_regs[PT_ORIG_R3] = random();
ucp->uc_mcontext.gp_regs[PT_XER] = random();
ucp->uc_mcontext.gp_regs[PT_RESULT] = random();
ucp->uc_mcontext.gp_regs[PT_SOFTE] = random();
ucp->uc_mcontext.gp_regs[PT_DSCR] = random();
ucp->uc_mcontext.gp_regs[PT_CTR] = random();
ucp->uc_mcontext.gp_regs[PT_LNK] = random();
ucp->uc_mcontext.gp_regs[PT_CCR] = random();
ucp->uc_mcontext.gp_regs[PT_REGS_COUNT] = random();
ucp->uc_link->uc_mcontext.gp_regs[PT_TRAP] = random();
ucp->uc_link->uc_mcontext.gp_regs[PT_DSISR] = random();
ucp->uc_link->uc_mcontext.gp_regs[PT_DAR] = random();
ucp->uc_link->uc_mcontext.gp_regs[PT_ORIG_R3] = random();
ucp->uc_link->uc_mcontext.gp_regs[PT_XER] = random();
ucp->uc_link->uc_mcontext.gp_regs[PT_RESULT] = random();
ucp->uc_link->uc_mcontext.gp_regs[PT_SOFTE] = random();
ucp->uc_link->uc_mcontext.gp_regs[PT_DSCR] = random();
ucp->uc_link->uc_mcontext.gp_regs[PT_CTR] = random();
ucp->uc_link->uc_mcontext.gp_regs[PT_LNK] = random();
ucp->uc_link->uc_mcontext.gp_regs[PT_CCR] = random();
ucp->uc_link->uc_mcontext.gp_regs[PT_REGS_COUNT] = random();
if (args & ARG_MESS_WITH_TM_BEFORE) {
if (one_in_chance(2))
mess_with_tm();
}
}
static void seg_signal_handler(int signo, siginfo_t *si, void *uc)
{
/* Clear exit for process that segfaults */
exit(0);
}
static void *sigfuz_test(void *thrid)
{
struct sigaction trap_sa, seg_sa;
int ret, i = 0;
pid_t t;
tmp_uc = malloc(sizeof(ucontext_t));
/* Main signal handler */
trap_sa.sa_flags = SA_SIGINFO;
trap_sa.sa_sigaction = trap_signal_handler;
/* SIGSEGV signal handler */
seg_sa.sa_flags = SA_SIGINFO;
seg_sa.sa_sigaction = seg_signal_handler;
/* The signal handler will enable MSR_TS */
sigaction(SIGUSR1, &trap_sa, NULL);
/* If it does not crash, it will segfault, avoid it to retest */
sigaction(SIGSEGV, &seg_sa, NULL);
while (i < count_max) {
t = fork();
if (t == 0) {
/* Once seed per process */
srand(time(NULL) + getpid());
if (args & ARG_MESS_WITH_TM_AT) {
if (one_in_chance(2))
mess_with_tm();
}
raise(SIGUSR1);
exit(0);
} else {
waitpid(t, &ret, 0);
}
if (!(args & ARG_FOREVER))
i++;
}
/* If not freed already, free now */
if (tmp_uc) {
free(tmp_uc);
tmp_uc = NULL;
}
return NULL;
}
static int signal_fuzzer(void)
{
int t, rc;
pthread_t *threads;
threads = malloc(nthread * sizeof(pthread_t));
for (t = 0; t < nthread; t++) {
rc = pthread_create(&threads[t], NULL, sigfuz_test,
(void *)&t);
if (rc)
perror("Thread creation error\n");
}
for (t = 0; t < nthread; t++) {
rc = pthread_join(threads[t], NULL);
if (rc)
perror("Thread join error\n");
}
free(threads);
return EXIT_SUCCESS;
}
static void show_help(char *name)
{
printf("%s: Sigfuzzer for powerpc\n", name);
printf("Usage:\n");
printf("\t-b\t Mess with TM before raising a SIGUSR1 signal\n");
printf("\t-a\t Mess with TM after raising a SIGUSR1 signal\n");
printf("\t-m\t Mess with MSR[TS] bits at mcontext\n");
printf("\t-x\t Mess with everything above\n");
printf("\t-f\t Run forever (Press ^C to Quit)\n");
printf("\t-i\t Amount of interactions. (Default = %d)\n", COUNT_MAX);
printf("\t-t\t Amount of threads. (Default = %d)\n", THREADS);
exit(-1);
}
int main(int argc, char **argv)
{
int opt;
while ((opt = getopt(argc, argv, "bamxt:fi:h")) != -1) {
if (opt == 'b') {
printf("Mess with TM before signal\n");
args |= ARG_MESS_WITH_TM_BEFORE;
} else if (opt == 'a') {
printf("Mess with TM at signal handler\n");
args |= ARG_MESS_WITH_TM_AT;
} else if (opt == 'm') {
printf("Mess with MSR[TS] bits in mcontext\n");
args |= ARG_MESS_WITH_MSR_AT;
} else if (opt == 'x') {
printf("Running with all options enabled\n");
args |= ARG_COMPLETE;
} else if (opt == 't') {
nthread = atoi(optarg);
printf("Threads = %d\n", nthread);
} else if (opt == 'f') {
args |= ARG_FOREVER;
printf("Press ^C to stop\n");
test_harness_set_timeout(-1);
} else if (opt == 'i') {
count_max = atoi(optarg);
printf("Running for %d interactions\n", count_max);
} else if (opt == 'h') {
show_help(argv[0]);
}
}
/* Default test suite */
if (!args)
args = ARG_COMPLETE;
return test_harness(signal_fuzzer, "signal_fuzzer");
}
@@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2015, Cyril Bur, IBM Corp.
*/
#include "basic_asm.h"
/* long signal_self(pid_t pid, int sig); */
FUNC_START(signal_self)
li r0,37 /* sys_kill */
/* r3 already has our pid in it */
/* r4 already has signal type in it */
sc
bc 4,3,1f
subfze r3,r3
1: blr
FUNC_END(signal_self)
/* long tm_signal_self(pid_t pid, int sig, int *ret); */
FUNC_START(tm_signal_self)
PUSH_BASIC_STACK(8)
std r5,STACK_FRAME_PARAM(0)(sp) /* ret */
tbegin.
beq 1f
tsuspend.
li r0,37 /* sys_kill */
/* r3 already has our pid in it */
/* r4 already has signal type in it */
sc
ld r5,STACK_FRAME_PARAM(0)(sp) /* ret */
bc 4,3,2f
subfze r3,r3
2: std r3,0(r5)
tabort. 0
tresume. /* Be nice to some cleanup, jumps back to tbegin then to 1: */
/*
* Transaction should be proper doomed and we should never get
* here
*/
li r3,1
POP_BASIC_STACK(8)
blr
1: li r3,0
POP_BASIC_STACK(8)
blr
FUNC_END(tm_signal_self)
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2016, Cyril Bur, IBM Corp.
*
* Sending one self a signal should always get delivered.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <altivec.h>
#include "utils.h"
#define MAX_ATTEMPT 500000
#define TIMEOUT 5
extern long signal_self(pid_t pid, int sig);
static sig_atomic_t signaled;
static sig_atomic_t fail;
static void signal_handler(int sig)
{
if (sig == SIGUSR1)
signaled = 1;
else
fail = 1;
}
static int test_signal()
{
int i;
struct sigaction act;
pid_t ppid = getpid();
pid_t pid;
act.sa_handler = signal_handler;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
if (sigaction(SIGUSR1, &act, NULL) < 0) {
perror("sigaction SIGUSR1");
exit(1);
}
if (sigaction(SIGALRM, &act, NULL) < 0) {
perror("sigaction SIGALRM");
exit(1);
}
/* Don't do this for MAX_ATTEMPT, its simply too long */
for(i = 0; i < 1000; i++) {
pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
}
if (pid == 0) {
signal_self(ppid, SIGUSR1);
exit(1);
} else {
alarm(0); /* Disable any pending */
alarm(2);
while (!signaled && !fail)
asm volatile("": : :"memory");
if (!signaled) {
fprintf(stderr, "Didn't get signal from child\n");
FAIL_IF(1); /* For the line number */
}
/* Otherwise we'll loop too fast and fork() will eventually fail */
waitpid(pid, NULL, 0);
}
}
for (i = 0; i < MAX_ATTEMPT; i++) {
long rc;
alarm(0); /* Disable any pending */
signaled = 0;
alarm(TIMEOUT);
rc = signal_self(ppid, SIGUSR1);
if (rc) {
fprintf(stderr, "(%d) Fail reason: %d rc=0x%lx",
i, fail, rc);
FAIL_IF(1); /* For the line number */
}
while (!signaled && !fail)
asm volatile("": : :"memory");
if (!signaled) {
fprintf(stderr, "(%d) Fail reason: %d rc=0x%lx",
i, fail, rc);
FAIL_IF(1); /* For the line number */
}
}
return 0;
}
int main(void)
{
test_harness_set_timeout(300);
return test_harness(test_signal, "signal");
}
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2016, Cyril Bur, IBM Corp.
*
* Sending one self a signal should always get delivered.
*/
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <altivec.h>
#include "utils.h"
#include "../tm/tm.h"
#define MAX_ATTEMPT 500000
#define TIMEOUT 10
extern long tm_signal_self(pid_t pid, int sig, long *ret);
static sig_atomic_t signaled;
static sig_atomic_t fail;
static void signal_handler(int sig)
{
if (tcheck_active()) {
fail = 2;
return;
}
if (sig == SIGUSR1)
signaled = 1;
else
fail = 1;
}
static int test_signal_tm()
{
int i;
struct sigaction act;
act.sa_handler = signal_handler;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
if (sigaction(SIGUSR1, &act, NULL) < 0) {
perror("sigaction SIGUSR1");
exit(1);
}
if (sigaction(SIGALRM, &act, NULL) < 0) {
perror("sigaction SIGALRM");
exit(1);
}
SKIP_IF(!have_htm());
SKIP_IF(htm_is_synthetic());
for (i = 0; i < MAX_ATTEMPT; i++) {
/*
* If anything bad happens in ASM and we fail to set ret
* because *handwave* TM this will cause failure
*/
long ret = 0xdead;
long rc = 0xbeef;
alarm(0); /* Disable any pending */
signaled = 0;
alarm(TIMEOUT);
FAIL_IF(tcheck_transactional());
rc = tm_signal_self(getpid(), SIGUSR1, &ret);
if (ret == 0xdead)
/*
* This basically means the transaction aborted before we
* even got to the suspend... this is crazy but it
* happens.
* Yes this also means we might never make forward
* progress... the alarm() will trip eventually...
*/
continue;
if (rc || ret) {
/* Ret is actually an errno */
printf("TEXASR 0x%016lx, TFIAR 0x%016lx\n",
__builtin_get_texasr(), __builtin_get_tfiar());
fprintf(stderr, "(%d) Fail reason: %d rc=0x%lx ret=0x%lx\n",
i, fail, rc, ret);
FAIL_IF(ret);
}
while(!signaled && !fail)
asm volatile("": : :"memory");
if (!signaled) {
fprintf(stderr, "(%d) Fail reason: %d rc=0x%lx ret=0x%lx\n",
i, fail, rc, ret);
FAIL_IF(fail); /* For the line number */
}
}
return 0;
}
int main(void)
{
return test_harness(test_signal_tm, "signal_tm");
}
@@ -0,0 +1,132 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Test that we can't sigreturn to kernel addresses, or to kernel mode.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "utils.h"
#define MSR_PR (1ul << 14)
static volatile unsigned long long sigreturn_addr;
static volatile unsigned long long sigreturn_msr_mask;
static void sigusr1_handler(int signo, siginfo_t *si, void *uc_ptr)
{
ucontext_t *uc = (ucontext_t *)uc_ptr;
if (sigreturn_addr)
UCONTEXT_NIA(uc) = sigreturn_addr;
if (sigreturn_msr_mask)
UCONTEXT_MSR(uc) &= sigreturn_msr_mask;
}
static pid_t fork_child(void)
{
pid_t pid;
pid = fork();
if (pid == 0) {
raise(SIGUSR1);
exit(0);
}
return pid;
}
static int expect_segv(pid_t pid)
{
int child_ret;
waitpid(pid, &child_ret, 0);
FAIL_IF(WIFEXITED(child_ret));
FAIL_IF(!WIFSIGNALED(child_ret));
FAIL_IF(WTERMSIG(child_ret) != 11);
return 0;
}
int test_sigreturn_kernel(void)
{
struct sigaction act;
int child_ret, i;
pid_t pid;
act.sa_sigaction = sigusr1_handler;
act.sa_flags = SA_SIGINFO;
sigemptyset(&act.sa_mask);
FAIL_IF(sigaction(SIGUSR1, &act, NULL));
for (i = 0; i < 2; i++) {
// Return to kernel
sigreturn_addr = 0xcull << 60;
pid = fork_child();
expect_segv(pid);
// Return to kernel virtual
sigreturn_addr = 0xc008ull << 48;
pid = fork_child();
expect_segv(pid);
// Return out of range
sigreturn_addr = 0xc010ull << 48;
pid = fork_child();
expect_segv(pid);
// Return to no-man's land, just below PAGE_OFFSET
sigreturn_addr = (0xcull << 60) - (64 * 1024);
pid = fork_child();
expect_segv(pid);
// Return to no-man's land, above TASK_SIZE_4PB
sigreturn_addr = 0x1ull << 52;
pid = fork_child();
expect_segv(pid);
// Return to 0xd space
sigreturn_addr = 0xdull << 60;
pid = fork_child();
expect_segv(pid);
// Return to 0xe space
sigreturn_addr = 0xeull << 60;
pid = fork_child();
expect_segv(pid);
// Return to 0xf space
sigreturn_addr = 0xfull << 60;
pid = fork_child();
expect_segv(pid);
// Attempt to set PR=0 for 2nd loop (should be blocked by kernel)
sigreturn_msr_mask = ~MSR_PR;
}
printf("All children killed as expected\n");
// Don't change address, just MSR, should return to user as normal
sigreturn_addr = 0;
sigreturn_msr_mask = ~MSR_PR;
pid = fork_child();
waitpid(pid, &child_ret, 0);
FAIL_IF(!WIFEXITED(child_ret));
FAIL_IF(WIFSIGNALED(child_ret));
FAIL_IF(WEXITSTATUS(child_ret) != 0);
return 0;
}
int main(void)
{
return test_harness(test_sigreturn_kernel, "sigreturn_kernel");
}
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Test sigreturn to an unaligned address, ie. low 2 bits set.
* Nothing bad should happen.
* This was able to trigger warnings with CONFIG_PPC_RFI_SRR_DEBUG=y.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>
#include "utils.h"
static void sigusr1_handler(int signo, siginfo_t *info, void *ptr)
{
ucontext_t *uc = ptr;
UCONTEXT_NIA(uc) |= 3;
}
static int test_sigreturn_unaligned(void)
{
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_sigaction = sigusr1_handler;
action.sa_flags = SA_SIGINFO;
FAIL_IF(sigaction(SIGUSR1, &action, NULL) == -1);
raise(SIGUSR1);
return 0;
}
int main(void)
{
return test_harness(test_sigreturn_unaligned, "sigreturn_unaligned");
}
@@ -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");
}