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 | |