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
51static 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
58static 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
69static int sig;
70static 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
81static void
82trigger_segv(void)
83{
84	volatile int *p = (int *)(intptr_t)atoi("0");
85
86	*p = 1;
87}
88
89static void
90trigger_trap(void)
91{
92
93#ifdef PTRACE_BREAKPOINT_ASM
94	PTRACE_BREAKPOINT_ASM;
95#else
96	/* port me */
97#endif
98}
99
100static void
101trigger_ill(void)
102{
103
104#ifdef PTRACE_ILLEGAL_ASM
105	PTRACE_ILLEGAL_ASM;
106#else
107	/* port me */
108#endif
109}
110
111static void
112check_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
131volatile int ignore_result;
132
133static void
134trigger_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
152static void
153trigger_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
177static void
178trigger(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
202static void
203foo(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
215static __dead void
216usage(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
226int
227main(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