• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /*
24 A signal handler is set up so that in most cases the machine exception
25 generated by the illegal syscall, bad operands, etc in the procedure
26 made up of random data are caught; and another round of randomness may
27 be tried. Eventually a random syscall may corrupt the program or
28 the machine state in such a way that the program must halt. This is
29 a test of the robustness of the hardware/software for instruction
30 fault handling.
31 
32 Note: Running this program just a few times, using total CPU time of
33 less than a few seconds SHOULD NOT GIVE YOU ANY CONFIDENCE in system
34 robustness. Having it run for hours, with tens of thousands of cases
35 would be a different thing. It would also make sense to run this
36 stress test at the same time you run other tests, like a multi-user
37 benchmark.
38 
39 CAUTION: 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 
47 TODO:
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 
67 char *TCID = "crash02";
68 int TST_TOTAL = 1;
69 
70 static int x_opt = 0;
71 static int v_opt = 0;
72 static char *v_copt;
73 static int s_opt = 0;
74 static char *s_copt;
75 static int l_opt = 0;
76 static char *l_copt;
77 static int n_opt = 0;
78 static char *n_copt;
79 
80 int verbose_level = 2;
81 
82 /* depends on architecture.. */
83 unsigned int sysno_max = 127;
84 
85 int nseed;
86 int ntries = 100;
87 
88 /* max time allowed per try, in seconds */
89 #define MAX_TRY_TIME 5
90 
cleanup(void)91 void cleanup(void)
92 {
93 
94 	tst_rmdir();
95 
96 }
97 
setup(void)98 void 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 
help(void)111 void 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  */
123 option_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 
133 void badboy_fork();
134 void badboy_loop();
135 
136 void summarize_errno();
137 void record_errno(unsigned int n);
138 
main(int argc,char * argv[])139 int 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 /* ************************* */
179 int badboy_pid;
180 
181 void my_signal(int sig, void (*func) ());
182 
monitor_fcn(int sig)183 void 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 
badboy_fork(void)197 void 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
240 static int errno_table[STATUS_MAX];
241 
record_errno(unsigned int n)242 void 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 */
summarize_errno(void)251 void 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 
267 jmp_buf again_buff;
268 
269 unsigned char *bad_malloc(int n);
270 void my_signal(int sig, void (*func) ());
271 void again_handler(int sig);
272 void try_one_crash(int try_num);
273 void set_up_signals();
274 int in_blacklist(int sysno);
275 
276 /* badboy "entry" point */
277 
278 /*
279  * Unlike crashme, faulty syscalls are not supposed to barf
280  */
badboy_loop(void)281 void 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 
again_handler(int sig)304 void 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 
my_signal(int sig,void (* func)())355 void 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 
set_up_signals(void)365 void 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  */
rand_long(void)400 long 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 
try_one_crash(int try_num)417 void 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. */
in_blacklist(int sysno)447 int 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