xref: /netbsd-src/tests/kernel/h_segv.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: h_segv.c,v 1.14 2019/04/25 19:37:09 kamil 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.14 2019/04/25 19:37:09 kamil 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
82 trigger_segv(void)
83 {
84 	volatile int *p = (int *)(intptr_t)atoi("0");
85 
86 	*p = 1;
87 }
88 
89 static 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
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
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 #endif
125 	exit(EXIT_SUCCESS);
126 }
127 
128 static void
129 trigger_fpe(void)
130 {
131 	volatile double a = getpid();
132 	volatile double b = strtol("0", NULL, 0);
133 
134 #ifdef __HAVE_FENV
135 	feenableexcept(FE_ALL_EXCEPT);
136 #endif
137 
138 	usleep((int)(a/b));
139 }
140 
141 static void
142 trigger_bus(void)
143 {
144 	FILE *fp;
145 	char *p;
146 
147 	/* Open an empty file for writing. */
148 	fp = tmpfile();
149 	if (fp == NULL)
150 		err(EXIT_FAILURE, "tmpfile");
151 
152 	/*
153 	 * Map an empty file with mmap(2) to a pointer.
154 	 *
155 	 * PROT_READ handles read-modify-write sequences emitted for
156 	 * certain combinations of CPUs and compilers (e.g. Alpha AXP).
157 	 */
158 	p = mmap(0, 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(fp), 0);
159 	if (p == MAP_FAILED)
160 		err(EXIT_FAILURE, "mmap");
161 
162 	/* Invalid memory access causes CPU trap, translated to SIGBUS */
163 	*p = 'a';
164 }
165 
166 static void
167 trigger(void)
168 {
169 
170 	switch (sig) {
171 	case SIGSEGV:
172 		trigger_segv();
173 		break;
174 	case SIGTRAP:
175 		trigger_trap();
176 		break;
177 	case SIGILL:
178 		trigger_ill();
179 		break;
180 	case SIGFPE:
181 		trigger_fpe();
182 		break;
183 	case SIGBUS:
184 		trigger_bus();
185 		break;
186 	default:
187 		break;
188 	}
189 }
190 
191 static void
192 foo(int s)
193 {
194 	char buf[64];
195 	int i = snprintf(buf, sizeof(buf), "got %d\n", s);
196 	write(2, buf, i);
197 
198 	if (flags & F_RECURSE)
199 		trigger();
200 
201 	exit(EXIT_SUCCESS);
202 }
203 
204 static __dead void
205 usage(void)
206 {
207 	const char *pname = getprogname();
208 
209 	fprintf(stderr, "Usage: %s segv|trap|ill|fpe|bus "
210 	                "[recurse|mask|handle|ignore|check] ...\n", pname);
211 
212 	exit(EXIT_FAILURE);
213 }
214 
215 int
216 main(int argc, char *argv[])
217 {
218 
219 	if (argc == 1)
220 	    usage();
221 
222 	for (int i = 1; i < argc; i++) {
223 		size_t j;
224 		for (j = 0; j < __arraycount(nv); j++) {
225 			if (strcmp(nv[j].n, argv[i]) == 0) {
226 				flags |= nv[j].v;
227 				goto consumed;
228 			}
229 		}
230 		for (j = 0; j < __arraycount(sn); j++) {
231 			if (strcmp(sn[j].n, argv[i]) == 0) {
232 				sig = sn[j].v;
233 				goto consumed;
234 			}
235 		}
236 
237 		usage();
238 
239 	consumed:
240 		continue;
241 	}
242 
243 	if (flags == 0 || sig == 0)
244 		usage();
245 
246 	if (flags & F_CHECK && sig != SIGFPE) {
247 		fprintf(stderr, "can only check for fpe support\n");
248 		return 1;
249 	}
250 	if (flags & F_CHECK)
251 		check_fpe();
252 
253 	if (flags & F_HANDLE) {
254 		struct sigaction sa;
255 
256 		sa.sa_flags = SA_RESTART;
257 		sa.sa_handler = foo;
258 		sigemptyset(&sa.sa_mask);
259 		if (sigaction(sig, &sa, NULL) == -1)
260 			err(EXIT_FAILURE, "sigaction");
261 	}
262 
263 	if (flags & F_MASK) {
264 		sigset_t set;
265 
266 		sigemptyset(&set);
267 		sigaddset(&set, sig);
268 		if (sigprocmask(SIG_BLOCK, &set, NULL) == -1)
269 			err(EXIT_FAILURE, "sigprocmask");
270 	}
271 
272 	if (flags & F_IGNORE) {
273 		struct sigaction sa;
274 
275 		memset(&sa, 0, sizeof(sa));
276 		sa.sa_handler = SIG_IGN;
277 		sigemptyset(&sa.sa_mask);
278 		if (sigaction(sig, &sa, NULL) == -1)
279 			err(EXIT_FAILURE, "sigaction");
280 	}
281 
282 	trigger();
283 
284 	return EXIT_SUCCESS;
285 }
286