-
Notifications
You must be signed in to change notification settings - Fork 5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
powerpc: Use trap metadata to prevent double restart rather than zero…
…ing trap It's not very nice to zero trap for this, because then system calls no longer have trap_is_syscall(regs) invariant, and we can't distinguish between sc and scv system calls (in a later patch). Take one last unused bit from the low bits of the pt_regs.trap word for this instead. There is not a really good reason why it should be in trap as opposed to another field, but trap has some concept of flags and it exists. Ideally I think we would move trap to 2-byte field and have 2 more bytes available independently. Add a selftests case for this, which can be seen to fail if trap_norestart() is changed to return false. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> [mpe: Make them static inlines] Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20200507121332.2233629-4-mpe@ellerman.id.au
- Loading branch information
Showing
6 changed files
with
201 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
174 changes: 174 additions & 0 deletions
174
tools/testing/selftests/powerpc/signal/sig_sc_double_restart.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"); | ||
} |