1 /* $NetBSD: h_segv.c,v 1.15 2024/05/14 15:54:16 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2017 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: h_segv.c,v 1.15 2024/05/14 15:54:16 riastradh Exp $");
33
34 #define __TEST_FENV
35
36 #include <sys/types.h>
37 #include <sys/mman.h>
38 #include <sys/ptrace.h>
39
40 #include <err.h>
41 #include <fenv.h>
42 #if (__arm__ && !__SOFTFP__) || __aarch64__
43 #include <ieeefp.h> /* only need for ARM Cortex/Neon hack */
44 #endif
45 #include <signal.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50
51 static int flags;
52 #define F_RECURSE 1
53 #define F_HANDLE 2
54 #define F_MASK 4
55 #define F_IGNORE 8
56 #define F_CHECK 16
57
58 static struct {
59 const char *n;
60 int v;
61 } nv[] = {
62 { "recurse", F_RECURSE },
63 { "handle", F_HANDLE },
64 { "mask", F_MASK },
65 { "ignore", F_IGNORE },
66 { "check", F_CHECK }
67 };
68
69 static int sig;
70 static struct {
71 const char *n;
72 int v;
73 } sn[] = {
74 { "segv", SIGSEGV },
75 { "trap", SIGTRAP },
76 { "ill", SIGILL },
77 { "fpe", SIGFPE },
78 { "bus", SIGBUS }
79 };
80
81 static void
trigger_segv(void)82 trigger_segv(void)
83 {
84 volatile int *p = (int *)(intptr_t)atoi("0");
85
86 *p = 1;
87 }
88
89 static void
trigger_trap(void)90 trigger_trap(void)
91 {
92
93 #ifdef PTRACE_BREAKPOINT_ASM
94 PTRACE_BREAKPOINT_ASM;
95 #else
96 /* port me */
97 #endif
98 }
99
100 static void
trigger_ill(void)101 trigger_ill(void)
102 {
103
104 #ifdef PTRACE_ILLEGAL_ASM
105 PTRACE_ILLEGAL_ASM;
106 #else
107 /* port me */
108 #endif
109 }
110
111 static void
check_fpe(void)112 check_fpe(void)
113 {
114 #if (__arm__ && !__SOFTFP__) || __aarch64__
115 /*
116 * Some NEON fpus do not trap on IEEE 754 FP exceptions.
117 * Skip these tests if running on them and compiled for
118 * hard float.
119 */
120 if (0 == fpsetmask(fpsetmask(FP_X_INV))) {
121 printf("FPU does not implement traps on FP exceptions\n");
122 exit(EXIT_FAILURE);
123 }
124 #elif defined __riscv__
125 printf("RISC-V does not support floating-point exception traps\n");
126 exit(EXIT_FAILURE);
127 #endif
128 exit(EXIT_SUCCESS);
129 }
130
131 volatile int ignore_result;
132
133 static void
trigger_fpe(void)134 trigger_fpe(void)
135 {
136 volatile double a = getpid();
137 volatile double b = strtol("0", NULL, 0);
138
139 #ifdef __HAVE_FENV
140 feenableexcept(FE_ALL_EXCEPT);
141 #endif
142
143 /*
144 * Try to trigger SIGFPE either by dividing by zero (which is
145 * defined to raise FE_DIVBYZERO, but may just return infinity
146 * without trapping the exception) or by converting infinity to
147 * integer.
148 */
149 ignore_result = (int)(a/b);
150 }
151
152 static void
trigger_bus(void)153 trigger_bus(void)
154 {
155 FILE *fp;
156 char *p;
157
158 /* Open an empty file for writing. */
159 fp = tmpfile();
160 if (fp == NULL)
161 err(EXIT_FAILURE, "tmpfile");
162
163 /*
164 * Map an empty file with mmap(2) to a pointer.
165 *
166 * PROT_READ handles read-modify-write sequences emitted for
167 * certain combinations of CPUs and compilers (e.g. Alpha AXP).
168 */
169 p = mmap(0, 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(fp), 0);
170 if (p == MAP_FAILED)
171 err(EXIT_FAILURE, "mmap");
172
173 /* Invalid memory access causes CPU trap, translated to SIGBUS */
174 *p = 'a';
175 }
176
177 static void
trigger(void)178 trigger(void)
179 {
180
181 switch (sig) {
182 case SIGSEGV:
183 trigger_segv();
184 break;
185 case SIGTRAP:
186 trigger_trap();
187 break;
188 case SIGILL:
189 trigger_ill();
190 break;
191 case SIGFPE:
192 trigger_fpe();
193 break;
194 case SIGBUS:
195 trigger_bus();
196 break;
197 default:
198 break;
199 }
200 }
201
202 static void
foo(int s)203 foo(int s)
204 {
205 char buf[64];
206 int i = snprintf(buf, sizeof(buf), "got %d\n", s);
207 write(2, buf, i);
208
209 if (flags & F_RECURSE)
210 trigger();
211
212 exit(EXIT_SUCCESS);
213 }
214
215 static __dead void
usage(void)216 usage(void)
217 {
218 const char *pname = getprogname();
219
220 fprintf(stderr, "Usage: %s segv|trap|ill|fpe|bus "
221 "[recurse|mask|handle|ignore|check] ...\n", pname);
222
223 exit(EXIT_FAILURE);
224 }
225
226 int
main(int argc,char * argv[])227 main(int argc, char *argv[])
228 {
229
230 if (argc == 1)
231 usage();
232
233 for (int i = 1; i < argc; i++) {
234 size_t j;
235 for (j = 0; j < __arraycount(nv); j++) {
236 if (strcmp(nv[j].n, argv[i]) == 0) {
237 flags |= nv[j].v;
238 goto consumed;
239 }
240 }
241 for (j = 0; j < __arraycount(sn); j++) {
242 if (strcmp(sn[j].n, argv[i]) == 0) {
243 sig = sn[j].v;
244 goto consumed;
245 }
246 }
247
248 usage();
249
250 consumed:
251 continue;
252 }
253
254 if (flags == 0 || sig == 0)
255 usage();
256
257 if (flags & F_CHECK && sig != SIGFPE) {
258 fprintf(stderr, "can only check for fpe support\n");
259 return 1;
260 }
261 if (flags & F_CHECK)
262 check_fpe();
263
264 if (flags & F_HANDLE) {
265 struct sigaction sa;
266
267 sa.sa_flags = SA_RESTART;
268 sa.sa_handler = foo;
269 sigemptyset(&sa.sa_mask);
270 if (sigaction(sig, &sa, NULL) == -1)
271 err(EXIT_FAILURE, "sigaction");
272 }
273
274 if (flags & F_MASK) {
275 sigset_t set;
276
277 sigemptyset(&set);
278 sigaddset(&set, sig);
279 if (sigprocmask(SIG_BLOCK, &set, NULL) == -1)
280 err(EXIT_FAILURE, "sigprocmask");
281 }
282
283 if (flags & F_IGNORE) {
284 struct sigaction sa;
285
286 memset(&sa, 0, sizeof(sa));
287 sa.sa_handler = SIG_IGN;
288 sigemptyset(&sa.sa_mask);
289 if (sigaction(sig, &sa, NULL) == -1)
290 err(EXIT_FAILURE, "sigaction");
291 }
292
293 trigger();
294
295 return EXIT_SUCCESS;
296 }
297