1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Test that a syscall does not get restarted twice, handled by trap_norestart() 4 * 5 * Based on Al's description, and a test for the bug fixed in this commit: 6 * 7 * commit 9a81c16b527528ad307843be5571111aa8d35a80 8 * Author: Al Viro <viro@zeniv.linux.org.uk> 9 * Date: Mon Sep 20 21:48:57 2010 +0100 10 * 11 * powerpc: fix double syscall restarts 12 * 13 * Make sigreturn zero regs->trap, make do_signal() do the same on all 14 * paths. As it is, signal interrupting e.g. read() from fd 512 (== 15 * ERESTARTSYS) with another signal getting unblocked when the first 16 * handler finishes will lead to restart one insn earlier than it ought 17 * to. Same for multiple signals with in-kernel handlers interrupting 18 * that sucker at the same time. Same for multiple signals of any kind 19 * interrupting that sucker on 64bit... 20 */ 21 #define _GNU_SOURCE 22 #include <sys/types.h> 23 #include <sys/wait.h> 24 #include <sys/syscall.h> 25 #include <unistd.h> 26 #include <signal.h> 27 #include <errno.h> 28 #include <stdlib.h> 29 #include <stdio.h> 30 #include <string.h> 31 32 #include "utils.h" 33 34 static void SIGUSR1_handler(int sig) 35 { 36 kill(getpid(), SIGUSR2); 37 /* 38 * SIGUSR2 is blocked until the handler exits, at which point it will 39 * be raised again and think there is a restart to be done because the 40 * pending restarted syscall has 512 (ERESTARTSYS) in r3. The second 41 * restart will retreat NIP another 4 bytes to fail case branch. 42 */ 43 } 44 45 static void SIGUSR2_handler(int sig) 46 { 47 } 48 49 static ssize_t raw_read(int fd, void *buf, size_t count) 50 { 51 register long nr asm("r0") = __NR_read; 52 register long _fd asm("r3") = fd; 53 register void *_buf asm("r4") = buf; 54 register size_t _count asm("r5") = count; 55 56 asm volatile( 57 " b 0f \n" 58 " b 1f \n" 59 " 0: sc 0 \n" 60 " bns 2f \n" 61 " neg %0,%0 \n" 62 " b 2f \n" 63 " 1: \n" 64 " li %0,%4 \n" 65 " 2: \n" 66 : "+r"(_fd), "+r"(nr), "+r"(_buf), "+r"(_count) 67 : "i"(-ENOANO) 68 : "memory", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "ctr", "cr0"); 69 70 if (_fd < 0) { 71 errno = -_fd; 72 _fd = -1; 73 } 74 75 return _fd; 76 } 77 78 #define DATA "test 123" 79 #define DLEN (strlen(DATA)+1) 80 81 int test_restart(void) 82 { 83 int pipefd[2]; 84 pid_t pid; 85 char buf[512]; 86 87 if (pipe(pipefd) == -1) { 88 perror("pipe"); 89 exit(EXIT_FAILURE); 90 } 91 92 pid = fork(); 93 if (pid == -1) { 94 perror("fork"); 95 exit(EXIT_FAILURE); 96 } 97 98 if (pid == 0) { /* Child reads from pipe */ 99 struct sigaction act; 100 int fd; 101 102 memset(&act, 0, sizeof(act)); 103 sigaddset(&act.sa_mask, SIGUSR2); 104 act.sa_handler = SIGUSR1_handler; 105 act.sa_flags = SA_RESTART; 106 if (sigaction(SIGUSR1, &act, NULL) == -1) { 107 perror("sigaction"); 108 exit(EXIT_FAILURE); 109 } 110 111 memset(&act, 0, sizeof(act)); 112 act.sa_handler = SIGUSR2_handler; 113 act.sa_flags = SA_RESTART; 114 if (sigaction(SIGUSR2, &act, NULL) == -1) { 115 perror("sigaction"); 116 exit(EXIT_FAILURE); 117 } 118 119 /* Let's get ERESTARTSYS into r3 */ 120 while ((fd = dup(pipefd[0])) != 512) { 121 if (fd == -1) { 122 perror("dup"); 123 exit(EXIT_FAILURE); 124 } 125 } 126 127 if (raw_read(fd, buf, 512) == -1) { 128 if (errno == ENOANO) { 129 fprintf(stderr, "Double restart moved restart before sc instruction.\n"); 130 _exit(EXIT_FAILURE); 131 } 132 perror("read"); 133 exit(EXIT_FAILURE); 134 } 135 136 if (strncmp(buf, DATA, DLEN)) { 137 fprintf(stderr, "bad test string %s\n", buf); 138 exit(EXIT_FAILURE); 139 } 140 141 return 0; 142 143 } else { 144 int wstatus; 145 146 usleep(100000); /* Hack to get reader waiting */ 147 kill(pid, SIGUSR1); 148 usleep(100000); 149 if (write(pipefd[1], DATA, DLEN) != DLEN) { 150 perror("write"); 151 exit(EXIT_FAILURE); 152 } 153 close(pipefd[0]); 154 close(pipefd[1]); 155 if (wait(&wstatus) == -1) { 156 perror("wait"); 157 exit(EXIT_FAILURE); 158 } 159 if (!WIFEXITED(wstatus)) { 160 fprintf(stderr, "child exited abnormally\n"); 161 exit(EXIT_FAILURE); 162 } 163 164 FAIL_IF(WEXITSTATUS(wstatus) != EXIT_SUCCESS); 165 166 return 0; 167 } 168 } 169 170 int main(void) 171 { 172 test_harness_set_timeout(10); 173 return test_harness(test_restart, "sig sys restart"); 174 } 175
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.