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