h_segv.c revision 1.6
1/*	$NetBSD: h_segv.c,v 1.6 2018/05/30 17:31:34 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.6 2018/05/30 17:31:34 kamil Exp $");
33
34#include <sys/types.h>
35#include <sys/mman.h>
36#include <sys/ptrace.h>
37#include <stdio.h>
38#include <string.h>
39#include <stdlib.h>
40#include <unistd.h>
41#include <signal.h>
42#include <err.h>
43
44static int flags;
45#define F_RECURSE 	1
46#define F_HANDLE	2
47#define F_MASK		4
48#define F_IGNORE	8
49
50static struct {
51	const char *n;
52	int v;
53} nv[] = {
54	{ "recurse",	F_RECURSE },
55	{ "handle",	F_HANDLE },
56	{ "mask",	F_MASK },
57	{ "ignore",	F_IGNORE }
58};
59
60static int sig;
61static struct {
62	const char *n;
63	int v;
64} sn[] = {
65	{ "segv",	SIGSEGV },
66	{ "trap",	SIGTRAP },
67	{ "ill",	SIGILL },
68	{ "fpe",	SIGFPE },
69	{ "bus",	SIGBUS }
70};
71
72static void
73trigger_segv(void)
74{
75	volatile int *p = (int *)(intptr_t)atoi("0");
76
77	*p = 1;
78}
79
80static void
81trigger_trap(void)
82{
83
84#ifdef PTRACE_BREAKPOINT_ASM
85	PTRACE_BREAKPOINT_ASM;
86#else
87	/* port me */
88#endif
89}
90
91static void
92trigger_ill(void)
93{
94
95#ifdef PTRACE_ILLEGAL_ASM
96	PTRACE_ILLEGAL_ASM;
97#else
98	/* port me */
99#endif
100}
101
102static void
103trigger_fpe(void)
104{
105	volatile int a = getpid();
106	volatile int b = strtol("0", NULL, 0);
107
108	usleep(a/b);
109}
110
111static void
112trigger_bus(void)
113{
114	FILE *fp;
115	char *p;
116
117	/* Open an empty file for writing. */
118	fp = tmpfile();
119	if (fp == NULL)
120		err(EXIT_FAILURE, "tmpfile");
121
122	/* Map an empty file with mmap(2) to a pointer. */
123	p = mmap(0, 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(fp), 0);
124	if (p == MAP_FAILED)
125		err(EXIT_FAILURE, "mmap");
126
127	/* Invalid memory access causes CPU trap, translated to SIGBUS */
128	*p = 'a';
129}
130
131static void
132trigger(void)
133{
134
135	switch (sig) {
136	case SIGSEGV:
137		trigger_segv();
138		break;
139	case SIGTRAP:
140		trigger_trap();
141		break;
142	case SIGILL:
143		trigger_ill();
144		break;
145	case SIGFPE:
146		trigger_fpe();
147		break;
148	case SIGBUS:
149		trigger_bus();
150		break;
151	default:
152		break;
153	}
154}
155
156static void
157foo(int s)
158{
159	char buf[64];
160	int i = snprintf(buf, sizeof(buf), "got %d\n", s);
161	write(2, buf, i);
162
163	if (flags & F_RECURSE)
164		trigger();
165
166	exit(EXIT_SUCCESS);
167}
168
169static __dead void
170usage(void)
171{
172	const char *pname = getprogname();
173
174	fprintf(stderr, "Usage: %s segv|trap|ill|fpe|bus "
175	                "[recurse|mask|handle|ignore] ...\n", pname);
176
177	exit(EXIT_FAILURE);
178}
179
180int
181main(int argc, char *argv[])
182{
183
184	if (argc == 1)
185	    usage();
186
187	for (int i = 1; i < argc; i++) {
188		size_t j;
189		for (j = 0; j < __arraycount(nv); j++) {
190			if (strcmp(nv[j].n, argv[i]) == 0) {
191				flags |= nv[j].v;
192				goto consumed;
193			}
194		}
195		for (j = 0; j < __arraycount(sn); j++) {
196			if (strcmp(sn[j].n, argv[i]) == 0) {
197				sig = sn[j].v;
198				goto consumed;
199			}
200		}
201
202		usage();
203
204	consumed:
205		continue;
206	}
207
208	if (flags == 0 || sig == 0)
209		usage();
210
211	if (flags & F_HANDLE) {
212		struct sigaction sa;
213
214		sa.sa_flags = SA_RESTART;
215		sa.sa_handler = foo;
216		sigemptyset(&sa.sa_mask);
217		if (sigaction(sig, &sa, NULL) == -1)
218			err(EXIT_FAILURE, "sigaction");
219	}
220
221	if (flags & F_MASK) {
222		sigset_t set;
223
224		sigemptyset(&set);
225		sigaddset(&set, sig);
226		if (sigprocmask(SIG_BLOCK, &set, NULL) == -1)
227			err(EXIT_FAILURE, "sigprocmask");
228	}
229
230	if (flags & F_IGNORE) {
231		struct sigaction sa;
232
233		memset(&sa, 0, sizeof(sa));
234		sa.sa_handler = SIG_IGN;
235		sigemptyset(&sa.sa_mask);
236		if (sigaction(sig, &sa, NULL) == -1)
237			err(EXIT_FAILURE, "sigaction");
238	}
239
240	trigger();
241
242	return EXIT_SUCCESS;
243}
244