1/*
2 * Copyright 2005-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#undef NDEBUG
8
9#include <assert.h>
10#include <errno.h>
11#include <signal.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <unistd.h>
16
17#include <OS.h>
18
19extern const char *__progname;
20
21// usage
22static const char *kUsage =
23	"%s <options> [ <mode> ]\n"
24	"Crashes in more or less inovative ways.\n"
25	"\n"
26	"Options:\n"
27	"  -d                   - call disable_debugger() first\n"
28	"  -f, --fork           - fork() and continue in the child\n"
29	"  -h, --help           - print this info text\n"
30	"  --multi              - crash in multiple threads\n"
31	"  --signal             - crash in a signal handler\n"
32	"  --thread             - crash in a separate thread\n"
33	"\n"
34	"Modes:\n"
35	"[general]\n"
36	"  segv                 - dereferences a null pointer (default)\n"
37	"  segv2                - strcmp() using a 0x1 pointer\n"
38	"  div                  - executes a division by zero\n"
39	"  debugger             - invokes debugger()\n"
40	"  assert               - failed assert(), which should invoke the\n"
41	"                         debugger\n"
42	"\n"
43	"[x86 specific]\n"
44	"  int3                 - executes the int3 (breakpoint) instruction\n"
45	"  protection           - executes an instruction that causes a general\n"
46	"                         protection exception\n";
47
48// application name
49const char *kAppName = __progname;
50
51static void
52print_usage(bool error)
53{
54	fprintf(error ? stderr : stdout, kUsage, kAppName);
55}
56
57
58static void
59print_usage_and_exit(bool error)
60{
61	print_usage(error);
62	exit(error ? 0 : 1);
63}
64
65
66static int
67crash_segv()
68{
69	int *a = 0;
70	*a = 0;
71	return 0;
72}
73
74static int
75crash_segv2()
76{
77	const char *str = (const char*)0x1;
78	return strcmp(str, "Test");
79}
80
81static int
82crash_div()
83{
84	int i = 0;
85	i = 1 / i;
86	return i;
87}
88
89static int
90crash_debugger()
91{
92	debugger("crashing_app() invoked debugger()");
93	return 0;
94}
95
96
97static int
98crash_assert()
99{
100	assert(0 > 1);
101	return 0;
102}
103
104
105#if __i386__
106
107static int
108crash_int3()
109{
110	asm("int3");
111	return 0;
112}
113
114static int
115crash_protection()
116{
117	asm("movl %0, %%dr7" : : "r"(0));
118	return 0;
119}
120
121#endif	// __i386__
122
123
124typedef int crash_function_t();
125
126
127struct Options {
128	Options()
129		:
130		inThread(false),
131		multipleThreads(false),
132		inSignalHandler(false),
133		disableDebugger(false),
134		fork(false)
135	{
136	}
137
138	crash_function_t*	function;
139	bool				inThread;
140	bool				multipleThreads;
141	bool				inSignalHandler;
142	bool				disableDebugger;
143	bool				fork;
144};
145
146static Options sOptions;
147
148
149static crash_function_t*
150get_crash_function(const char* mode)
151{
152	if (strcmp(mode, "segv") == 0) {
153		return crash_segv;
154	} else if (strcmp(mode, "segv2") == 0) {
155		return crash_segv2;
156	} else if (strcmp(mode, "div") == 0) {
157		return (crash_function_t*)crash_div;
158	} else if (strcmp(mode, "debugger") == 0) {
159		return crash_debugger;
160	} else if (strcmp(mode, "assert") == 0) {
161		return crash_assert;
162#if __i386__
163	} else if (strcmp(mode, "int3") == 0) {
164		return crash_int3;
165	} else if (strcmp(mode, "protection") == 0) {
166		return crash_protection;
167#endif	// __i386__
168	}
169
170	return NULL;
171}
172
173
174static void
175signal_handler(int signal)
176{
177	sOptions.function();
178}
179
180
181static void
182do_crash()
183{
184	if (sOptions.inSignalHandler) {
185		signal(SIGUSR1, &signal_handler);
186		send_signal(find_thread(NULL), SIGUSR1);
187	} else
188		sOptions.function();
189}
190
191
192static status_t
193crashing_thread(void* data)
194{
195	snooze(100000);
196	do_crash();
197	return 0;
198}
199
200
201int
202main(int argc, const char* const* argv)
203{
204	const char* mode = "segv";
205
206	// parse args
207	int argi = 1;
208	while (argi < argc) {
209		const char *arg = argv[argi++];
210
211		if (arg[0] == '-') {
212			if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
213				print_usage_and_exit(false);
214			} else if (strcmp(arg, "-d") == 0) {
215				sOptions.disableDebugger = true;
216			} else if (strcmp(arg, "-f") == 0 || strcmp(arg, "--fork") == 0) {
217				sOptions.fork = true;
218			} else if (strcmp(arg, "--multi") == 0) {
219				sOptions.inThread = true;
220				sOptions.multipleThreads = true;
221			} else if (strcmp(arg, "--signal") == 0) {
222				sOptions.inSignalHandler = true;
223			} else if (strcmp(arg, "--thread") == 0) {
224				sOptions.inThread = true;
225			} else {
226				fprintf(stderr, "Invalid option \"%s\"\n", arg);
227				print_usage_and_exit(true);
228			}
229		} else {
230			mode = arg;
231		}
232	}
233
234	sOptions.function = get_crash_function(mode);
235	if (sOptions.function == NULL) {
236		fprintf(stderr, "Invalid mode \"%s\"\n", mode);
237		print_usage_and_exit(true);
238	}
239
240	if (sOptions.disableDebugger)
241		disable_debugger(true);
242
243	if (sOptions.fork) {
244		pid_t child = fork();
245		if (child < 0) {
246			fprintf(stderr, "fork() failed: %s\n", strerror(errno));
247			exit(1);
248		}
249
250		if (child > 0) {
251			// the parent exits
252			exit(1);
253		}
254
255		// the child continues...
256	}
257
258	if (sOptions.inThread) {
259		thread_id thread = spawn_thread(crashing_thread, "crashing thread",
260			B_NORMAL_PRIORITY, NULL);
261		if (thread < 0) {
262			fprintf(stderr, "Error: Failed to spawn thread: %s\n",
263				strerror(thread));
264			exit(1);
265		}
266
267		resume_thread(thread);
268
269		if (sOptions.multipleThreads) {
270			snooze(200000);
271			do_crash();
272		} else {
273			status_t result;
274			while (wait_for_thread(thread, &result) == B_INTERRUPTED) {
275			}
276		}
277	} else {
278		do_crash();
279	}
280
281	return 0;
282}
283