1/*
2 * crash02.c - Test OS robustness by executing syscalls with random args.
3 *
4 * Copyright (C) 2001 Stephane Fillod <f4cfe@free.fr>
5 *
6 * This test program was inspired from crashme, by GEORGE J. CARRETT.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA	02111-1307, USA.
21 */
22
23/*
24A signal handler is set up so that in most cases the machine exception
25generated by the illegal syscall, bad operands, etc in the procedure
26made up of random data are caught; and another round of randomness may
27be tried. Eventually a random syscall may corrupt the program or
28the machine state in such a way that the program must halt. This is
29a test of the robustness of the hardware/software for instruction
30fault handling.
31
32Note: Running this program just a few times, using total CPU time of
33less than a few seconds SHOULD NOT GIVE YOU ANY CONFIDENCE in system
34robustness. Having it run for hours, with tens of thousands of cases
35would be a different thing. It would also make sense to run this
36stress test at the same time you run other tests, like a multi-user
37benchmark.
38
39CAUTION: running this program may crash your system, your disk and all
40	your data along! DO NOT RUN IT ON PRODUCTION SYSTEMS!
41	CONSIDER YOUR DISK FRIED.
42	REMEMBER THE DISCLAIMER PART OF THE LICENSE.
43
44	Running as user nobody and with all your filesystems
45	remounted to readonly may be wise..
46
47TODO:
48	* in rand_long(), stuff in some real pointers to random data
49	* Does a syscall is supposed to send SIGSEGV?
50*/
51
52#define _GNU_SOURCE
53#include <sys/syscall.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <signal.h>
58#include <setjmp.h>
59#include <time.h>
60#include <unistd.h>
61#include <errno.h>
62#include <sys/types.h>
63#include <sys/wait.h>
64
65#include "test.h"
66
67char *TCID = "crash02";
68int TST_TOTAL = 1;
69
70static int x_opt = 0;
71static int v_opt = 0;
72static char *v_copt;
73static int s_opt = 0;
74static char *s_copt;
75static int l_opt = 0;
76static char *l_copt;
77static int n_opt = 0;
78static char *n_copt;
79
80int verbose_level = 2;
81
82/* depends on architecture.. */
83unsigned int sysno_max = 127;
84
85int nseed;
86int ntries = 100;
87
88/* max time allowed per try, in seconds */
89#define MAX_TRY_TIME 5
90
91void cleanup(void)
92{
93
94	tst_rmdir();
95
96}
97
98void setup(void)
99{
100	/*
101	 * setup a default signal hander and a
102	 * temporary working directory.
103	 */
104	tst_sig(FORK, DEF_HANDLER, cleanup);
105
106	TEST_PAUSE;
107
108	tst_tmpdir();
109}
110
111void help(void)
112{
113	printf
114	    ("	-x		dry run, hexdump random code instead\n");
115	printf("	-l x		max syscall no\n");
116	printf("	-v x		verbose level\n");
117	printf("	-s x		random seed\n");
118	printf("	-n x		ntries\n");
119}
120
121/*
122 */
123option_t options[] = {
124	{"v:", &v_opt, &v_copt},
125	{"l:", &l_opt, &l_copt},
126	{"s:", &s_opt, &s_copt},
127	{"n:", &n_opt, &n_copt},
128	{"x", &x_opt, NULL},
129
130	{NULL, NULL, NULL}
131};
132
133void badboy_fork();
134void badboy_loop();
135
136void summarize_errno();
137void record_errno(unsigned int n);
138
139int main(int argc, char *argv[])
140{
141	int lc;
142
143	tst_parse_opts(argc, argv, options, help);
144
145	if (v_opt)
146		verbose_level = atoi(v_copt);
147
148	if (n_opt)
149		ntries = atoi(n_copt);
150
151	if (l_opt)
152		sysno_max = atoi(l_copt);
153
154	if (s_opt)
155		nseed = atoi(s_copt);
156	else
157		nseed = time(NULL);
158
159	setup();
160
161	for (lc = 0; TEST_LOOPING(lc); lc++) {
162		tst_count = 0;
163
164		tst_resm(TINFO, "crashme02 %d %d %d", sysno_max, nseed, ntries);
165
166		srand(nseed);
167		badboy_fork();
168
169		/* still there? */
170		tst_resm(TPASS, "we're still here, OS seems to be robust");
171
172		nseed++;
173	}
174	cleanup();
175	tst_exit();
176}
177
178/* ************************* */
179int badboy_pid;
180
181void my_signal(int sig, void (*func) ());
182
183void monitor_fcn(int sig)
184{
185	int status;
186
187	if (verbose_level >= 3)
188		printf("time limit reached on pid. using kill.\n");
189
190	status = kill(badboy_pid, SIGKILL);
191	if (status < 0) {
192		if (verbose_level >= 3)
193			printf("failed to kill process\n");
194	}
195}
196
197void badboy_fork(void)
198{
199	int status, pid;
200	pid_t child;
201	child = fork();
202	badboy_pid = status;
203	switch (child) {
204	case -1:
205		perror("fork");
206	case 0:
207#ifdef DEBUG_LATE_BADBOY
208		sleep(ntries * MAX_TRY_TIME + 10);
209#else
210		badboy_loop();
211#endif
212		exit(0);
213	default:
214		if (verbose_level > 3)
215			printf("badboy pid = %d\n", badboy_pid);
216
217		/* don't trust the child to return at night */
218		my_signal(SIGALRM, monitor_fcn);
219		alarm(ntries * MAX_TRY_TIME);
220
221		pid = waitpid(-1, &status, WUNTRACED);
222		if (pid <= 0)
223			perror("wait");
224		else {
225			if (verbose_level > 3)
226				printf("pid %d exited with status %d\n",
227				       pid, status);
228#if 0
229			record_status(status);
230#endif
231		}
232	}
233	alarm(0);
234}
235
236/* *************** status recording ************************* */
237
238/* errno status table (max is actually around 127) */
239#define STATUS_MAX 256
240static int errno_table[STATUS_MAX];
241
242void record_errno(unsigned int n)
243{
244	if (n >= STATUS_MAX)
245		return;
246
247	errno_table[n]++;
248}
249
250/* may not work with -c option */
251void summarize_errno(void)
252{
253	int i;
254
255	if (x_opt || verbose_level < 2)
256		return;
257
258	printf("errno status ... number of cases\n");
259	for (i = 0; i < STATUS_MAX; i++) {
260		if (errno_table[i])
261			printf("%12d ... %5d\n", i, errno_table[i]);
262	}
263}
264
265/* ************* badboy ******************************************* */
266
267jmp_buf again_buff;
268
269unsigned char *bad_malloc(int n);
270void my_signal(int sig, void (*func) ());
271void again_handler(int sig);
272void try_one_crash(int try_num);
273void set_up_signals();
274int in_blacklist(int sysno);
275
276/* badboy "entry" point */
277
278/*
279 * Unlike crashme, faulty syscalls are not supposed to barf
280 */
281void badboy_loop(void)
282{
283	int i;
284
285	for (i = 0; i < ntries; ++i) {
286		/* level 5 */
287
288		if (!x_opt && verbose_level >= 5) {
289			printf("try %d\n", i);
290		}
291
292		if (setjmp(again_buff) == 3) {
293			if (verbose_level >= 5)
294				printf("Barfed\n");
295		} else {
296			set_up_signals();
297			alarm(MAX_TRY_TIME);
298			try_one_crash(i);
299		}
300	}
301	summarize_errno();
302}
303
304void again_handler(int sig)
305{
306	char *ss;
307
308	switch (sig) {
309	case SIGILL:
310		ss = " illegal instruction";
311		break;
312#ifdef SIGTRAP
313	case SIGTRAP:
314		ss = " trace trap";
315		break;
316#endif
317	case SIGFPE:
318		ss = " arithmetic exception";
319		break;
320#ifdef SIGBUS
321	case SIGBUS:
322		ss = " bus error";
323		break;
324#endif
325	case SIGSEGV:
326		ss = " segmentation violation";
327		break;
328#ifdef SIGIOT
329	case SIGIOT:
330		ss = " IOT instruction";
331		break;
332#endif
333#ifdef SIGEMT
334	case SIGEMT:
335		ss = " EMT instruction";
336		break;
337#endif
338#ifdef SIGALRM
339	case SIGALRM:
340		ss = " alarm clock";
341		break;
342#endif
343	case SIGINT:
344		ss = " interrupt";
345		break;
346	default:
347		ss = "";
348	}
349	if (verbose_level >= 5)
350		printf("Got signal %d%s\n", sig, ss);
351
352	longjmp(again_buff, 3);
353}
354
355void my_signal(int sig, void (*func) ())
356{
357	struct sigaction act;
358
359	act.sa_handler = func;
360	memset(&act.sa_mask, 0x00, sizeof(sigset_t));
361	act.sa_flags = SA_NOMASK | SA_RESTART;
362	sigaction(sig, &act, 0);
363}
364
365void set_up_signals(void)
366{
367	my_signal(SIGILL, again_handler);
368#ifdef SIGTRAP
369	my_signal(SIGTRAP, again_handler);
370#endif
371	my_signal(SIGFPE, again_handler);
372#ifdef SIGBUS
373	my_signal(SIGBUS, again_handler);
374#endif
375	my_signal(SIGSEGV, again_handler);
376#ifdef SIGIOT
377	my_signal(SIGIOT, again_handler);
378#endif
379#ifdef SIGEMT
380	my_signal(SIGEMT, again_handler);
381#endif
382#ifdef SIGALRM
383	my_signal(SIGALRM, again_handler);
384#endif
385	my_signal(SIGINT, again_handler);
386}
387
388/*
389 * NB: rand() (ie. RAND_MAX) might be on 31bits only!
390 *
391 * FIXME: 64-bit systems
392 *
393 * TODO: improve arg mixing (16bits and 8bits values, NULLs, etc.).
394 *	big values as returned by rand() are no so interresting
395 *	(except when used as pointers) because they may fall too
396 *	quickly in the invalid parameter sieve. Smaller values,
397 *	will be more insidious because they may refer to existing
398 *	objects (pids, fd, etc.).
399 */
400long int rand_long(void)
401{
402	int r1, r2;
403
404	r1 = rand();
405	r2 = rand();
406
407	if (r1 & 0x10000L)
408		r1 = 0;
409	if (!r1 && (r2 & 0x50000L))
410		r2 = 0;
411	else if (!r1 && (r2 & 0x20000L))
412		r2 &= 0x00ffL;
413
414	return (long int)((r1 & 0xffffL) << 16) | (r2 & 0xffffL);
415}
416
417void try_one_crash(int try_num)
418{
419	long int sysno, arg1, arg2, arg3, arg4, arg5, arg6, arg7;
420
421	do {
422		sysno = rand() % sysno_max;
423	} while (in_blacklist(sysno));
424
425	arg1 = rand_long();
426	arg2 = rand_long();
427	arg3 = rand_long();
428	arg4 = rand_long();
429	arg5 = rand_long();
430	arg6 = rand_long();
431	arg7 = rand_long();
432
433	if (x_opt) {
434		if (verbose_level >= 1)
435			printf("%04d: syscall(%ld, %#lx, %#lx, %#lx, %#lx, "
436			       "%#lx, %#lx, %#lx)\n",
437			       try_num, sysno, arg1, arg2, arg3, arg4, arg5,
438			       arg6, arg7);
439	} else {
440		syscall(sysno, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
441		record_errno(errno);
442	}
443}
444
445/* The following syscalls create new processes which may cause the test
446	 unable to finish. */
447int in_blacklist(int sysno)
448{
449	int i;
450	const int list[] = {
451#if defined(__ia64__)
452		SYS_clone2,
453#else
454		/*
455		 * No SYS_fork(vfork) on IA-64. Instead, it uses,
456		 * clone(child_stack=0, flags=CLONE_VM|CLONE_VFORK|SIGCHLD)
457		 * clone2()
458		 */
459
460		/*
461		 * NOTE (garrcoop):
462		 * Could not find reference to SYS_fork(vfork) on mips32
463		 * with the Montavista / Octeon toolchain. Need to develop an
464		 * autoconf check for this item.
465		 */
466#if defined(__NR_vfork) && __NR_vfork
467		SYS_vfork,
468#elif defined(__NR_fork) && __NR_fork
469		SYS_fork,
470#endif
471#endif /* __ia64__ */
472#if defined(__NR_clone) && __NR_clone
473		SYS_clone,
474#endif
475		-1
476	};
477
478	for (i = 0; list[i] != -1; i++) {
479		if (sysno == list[i])
480			return 1;
481	}
482
483	return 0;
484}
485