• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * crash01.c - Test OS robustness by creating a string of random bytes and then jump to it.
3  *
4  * New version Copyright (C) 2001 Stephane Fillod <f4cfe@free.fr>
5  *
6  * Original idea (c) 1990-1994 by GEORGE J. CARRETTE, CONCORD, MASSACHUSETTS.
7  *	from crashme version: "2.4 20-MAY-1994" <GJC@WORLD.STD.COM>
8  */
9 /* TODO: trapme: forge syscall with random args, and run it!! --SF */
10 
11 /*
12  *             COPYRIGHT (c) 1990-1994 BY        *
13  *  GEORGE J. CARRETTE, CONCORD, MASSACHUSETTS.  *
14  *             ALL RIGHTS RESERVED               *
15 
16 Permission to use, copy, modify, distribute and sell this software
17 and its documentation for any purpose and without fee is hereby
18 granted, provided that the above copyright notice appear in all copies
19 and that both that copyright notice and this permission notice appear
20 in supporting documentation, and that the name of the author
21 not be used in advertising or publicity pertaining to distribution
22 of the software without specific, written prior permission.
23 
24 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
26 HE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
27 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
28 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
29 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 SOFTWARE.
31 
32 */
33 
34 /*
35 
36 A signal handler is set up so that in most cases the machine exception
37 generated by the illegal instructions, bad operands, etc in the procedure
38 made up of random data are caught; and another round of randomness may
39 be tried. Eventually a random instruction may corrupt the program or
40 the machine state in such a way that the program must halt. This is
41 a test of the robustness of the hardware/software for instruction
42 fault handling.
43 
44 Note: Running this program just a few times, using total CPU time of
45 less than a few seconds SHOULD NOT GIVE YOU ANY CONFIDENCE in system
46 robustness. Having it run for hours, with tens of thousands of cases
47 would be a different thing. It would also make sense to run this
48 stress test at the same time you run other tests, like a multi-user
49 benchmark.
50 
51 */
52 
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <signal.h>
57 #include <setjmp.h>
58 #include <time.h>
59 #include <unistd.h>
60 #include <sys/types.h>
61 #include <sys/wait.h>
62 
63 #include "test.h"
64 
65 char *TCID = "crash01";
66 int TST_TOTAL = 1;
67 
68 static int x_opt = 0;
69 static int v_opt = 0;
70 static char *v_copt;
71 static int s_opt = 0;
72 static char *s_copt;
73 static int b_opt = 0;
74 static char *b_copt;
75 static int n_opt = 0;
76 static char *n_copt;
77 
78 int verbose_level = 2;
79 
80 /* Also, it may spend more time trapping and less time computing random bytes
81  * by using the smallest incptr (while not executing already tested bits).
82  */
83 int incptr = 80;
84 
85 int nseed;
86 int ntries = 100;
87 
88 /* compute block of nbytes at a time */
89 const int nbytes = 2000;
90 
91 /* max time allowed per try, in seconds */
92 #define MAX_TRY_TIME 5
93 
94 /* in % */
95 #define BLOCK_TRIGGER 80
96 
cleanup(void)97 void cleanup(void)
98 {
99 
100 	tst_rmdir();
101 
102 }
103 
setup(void)104 void setup(void)
105 {
106 	/*
107 	 * setup a default signal hander and a
108 	 * temporary working directory.
109 	 */
110 	tst_sig(FORK, DEF_HANDLER, cleanup);
111 
112 	tst_tmpdir();
113 
114 	TEST_PAUSE;
115 }
116 
help(void)117 void help(void)
118 {
119 	printf("  -x      dry run, hexdump random code instead\n");
120 	printf("  -v x    verbose level\n");
121 	printf("  -s x    random seed\n");
122 	printf("  -n x    ntries\n");
123 	printf("  -b x    inc\n");
124 }
125 
126 /*
127  * crashme [+]<nbytes>[.inc] <srand> <ntries> [nsub] [verbose]"
128  *
129  * crashme <-b [+]<nbytes>[.inc]> <-s srand> <-n ntries> [-v verbose]"
130  *  crashme +2000.80 666 100 1:10:30 2
131  *	nsub  -> -c ?
132  */
133 option_t options[] = {
134 	{"v:", &v_opt, &v_copt},
135 	{"s:", &s_opt, &s_copt},
136 	{"n:", &n_opt, &n_copt},
137 	{"b:", &b_opt, &b_copt},
138 	{"x", &x_opt, NULL},
139 
140 	{NULL, NULL, NULL}
141 };
142 
143 int malloc_flag = 1;		/* to be phased out */
144 
145 void badboy_fork();
146 void badboy_loop();
147 void summarize_status();
148 void record_status(unsigned int n);
149 
main(int argc,char * argv[])150 int main(int argc, char *argv[])
151 {
152 	int lc;
153 
154 	tst_parse_opts(argc, argv, options, help);
155 
156 	if (v_opt)
157 		verbose_level = atoi(v_copt);
158 
159 	if (n_opt)
160 		ntries = atoi(n_copt);
161 
162 	if (s_opt)
163 		nseed = atoi(s_copt);
164 	else
165 		nseed = time(NULL);
166 
167 	if (b_opt) {
168 		int inc;
169 
170 		inc = atoi(b_copt);
171 		if (inc <= nbytes / 2)
172 			incptr = inc;
173 		else
174 			tst_brkm(TBROK, cleanup,
175 				 "Invalid arg for -b (max: %u): %s", nbytes / 2,
176 				 b_copt);
177 	}
178 
179 	setup();
180 
181 	for (lc = 0; TEST_LOOPING(lc); lc++) {
182 		tst_count = 0;
183 
184 		tst_resm(TINFO, "crashme %s%d.%d %d %d",
185 			 (malloc_flag == 0) ? "" : "+", nbytes, incptr, nseed,
186 			 ntries);
187 
188 		srand(nseed);
189 		badboy_fork();
190 
191 		/* still there? */
192 		tst_resm(TPASS, "we're still here, OS seems to be robust");
193 
194 		nseed++;
195 	}
196 	summarize_status();
197 	cleanup();
198 	tst_exit();
199 }
200 
201 /* ************************* */
202 int badboy_pid;
203 
204 void my_signal(int sig, void (*func) ());
205 
monitor_fcn(int sig)206 void monitor_fcn(int sig)
207 {
208 	int status;
209 
210 	if (verbose_level >= 3)
211 		printf("time limit reached on pid. using kill.\n");
212 
213 	status = kill(badboy_pid, SIGKILL);
214 	if (status < 0) {
215 		if (verbose_level >= 3)
216 			printf("failed to kill process\n");
217 	}
218 }
219 
badboy_fork(void)220 void badboy_fork(void)
221 {
222 	int status, pid;
223 
224 	status = fork();
225 	badboy_pid = status;
226 	if (status == 0) {	/* badboy */
227 #ifdef DEBUG_LATE_BADBOY
228 		sleep(ntries * MAX_TRY_TIME + 10);
229 #else
230 		badboy_loop();
231 #endif
232 		exit(0);	/* good goy, he survived! */
233 	} else if (status < 0)
234 		perror("fork");
235 	else {			/* parent watching over badboy */
236 
237 		if (verbose_level > 3)
238 			printf("badboy pid = %d\n", badboy_pid);
239 
240 /* don't trust the child to return at night */
241 		my_signal(SIGALRM, monitor_fcn);
242 		alarm(ntries * MAX_TRY_TIME);
243 
244 		pid = wait(&status);
245 		if (pid <= 0) {
246 			perror("wait");
247 		} else {
248 			if (verbose_level > 3)
249 				printf("pid %d exited with status %d\n", pid,
250 				       status);
251 			record_status(status);
252 		}
253 	}			/* parent */
254 	alarm(0);
255 }
256 
257 /* *************** status recording ************************* */
258 
259 #define STATUS_MAX 256
260 static int status_table[STATUS_MAX];
261 
record_status(unsigned int n)262 void record_status(unsigned int n)
263 {
264 	if (n >= STATUS_MAX)
265 		return;
266 
267 	status_table[n]++;
268 }
269 
270 /* may not work with -c option */
summarize_status(void)271 void summarize_status(void)
272 {
273 	int i;
274 
275 	if (verbose_level < 2)
276 		return;
277 
278 	printf("exit status ... number of cases\n");
279 	for (i = 0; i < STATUS_MAX; i++) {
280 		if (status_table[i])
281 			printf("%11d ... %5d\n", i, status_table[i]);
282 	}
283 }
284 
285 /* ************* badboy ******************************************* */
286 
287 jmp_buf again_buff;
288 
289 typedef void (*BADBOY) ();
290 
291 BADBOY badboy;
292 char *the_data;
293 
294 int offset = 0;
295 int next_offset = 0;
296 
297 char *bad_malloc(int n);
298 void my_signal(int sig, void (*func) ());
299 void again_handler(int sig);
300 void compute_block_badboy(int n);
301 void compute_badboy();
302 BADBOY castaway(char *dat);
303 void try_one_crash();
304 void set_up_signals();
305 
306 /* badboy "entry" point */
badboy_loop(void)307 void badboy_loop(void)
308 {
309 	int i;
310 
311 	if (malloc_flag == 0) {
312 		the_data = bad_malloc((nbytes < 0) ? -nbytes : nbytes);
313 		badboy = castaway(the_data);
314 		printf("Badboy at %p\n", badboy);
315 	}
316 
317 	for (i = 0; i < ntries; ++i) {
318 		compute_badboy();
319 		/* level 5 */
320 
321 		if (!x_opt && verbose_level >= 5) {
322 			if (offset)
323 				printf("try %d, offset %d\n", i, offset);
324 			else if (malloc_flag == 1)
325 				printf("try %d, Badboy at %p\n", i, badboy);
326 			else
327 				printf("try %d\n", i);
328 		}
329 
330 		if (setjmp(again_buff) == 3) {
331 			if (verbose_level >= 5)
332 				printf("Barfed\n");
333 		} else {
334 			set_up_signals();
335 			alarm(MAX_TRY_TIME);
336 			try_one_crash();
337 			if (!x_opt && verbose_level >= 5)
338 				printf("didn't barf!\n");
339 		}
340 	}
341 }
342 
bad_malloc(int n)343 char *bad_malloc(int n)
344 {
345 	char *data;
346 	data = malloc(n);
347 #ifdef pyr
348 	if (mprotect(((int)data / PAGSIZ) * PAGSIZ, (n / PAGSIZ + 1) * PAGSIZ,
349 		     PROT_READ | PROT_WRITE | PROT_EXEC))
350 		perror("mprotect");
351 #endif
352 	return (data);
353 }
354 
again_handler(int sig)355 void again_handler(int sig)
356 {
357 	char *ss;
358 
359 	switch (sig) {
360 	case SIGILL:
361 		ss = " illegal instruction";
362 		break;
363 #ifdef SIGTRAP
364 	case SIGTRAP:
365 		ss = " trace trap";
366 		break;
367 #endif
368 	case SIGFPE:
369 		ss = " arithmetic exception";
370 		break;
371 #ifdef SIGBUS
372 	case SIGBUS:
373 		ss = " bus error";
374 		break;
375 #endif
376 	case SIGSEGV:
377 		ss = " segmentation violation";
378 		break;
379 #ifdef SIGIOT
380 	case SIGIOT:
381 		ss = " IOT instruction";
382 		break;
383 #endif
384 #ifdef SIGEMT
385 	case SIGEMT:
386 		ss = " EMT instruction";
387 		break;
388 #endif
389 #ifdef SIGALRM
390 	case SIGALRM:
391 		ss = " alarm clock";
392 		break;
393 #endif
394 	case SIGINT:
395 		ss = " interrupt";
396 		break;
397 	default:
398 		ss = "";
399 	}
400 	if (verbose_level >= 5)
401 		printf("Got signal %d%s\n", sig, ss);
402 
403 	longjmp(again_buff, 3);
404 }
405 
my_signal(int sig,void (* func)())406 void my_signal(int sig, void (*func) ())
407 {
408 	struct sigaction act;
409 
410 	act.sa_handler = func;
411 	memset(&act.sa_mask, 0x00, sizeof(sigset_t));
412 	act.sa_flags = SA_NOMASK | SA_RESTART;
413 	sigaction(sig, &act, 0);
414 }
415 
set_up_signals(void)416 void set_up_signals(void)
417 {
418 	my_signal(SIGILL, again_handler);
419 #ifdef SIGTRAP
420 	my_signal(SIGTRAP, again_handler);
421 #endif
422 	my_signal(SIGFPE, again_handler);
423 #ifdef SIGBUS
424 	my_signal(SIGBUS, again_handler);
425 #endif
426 	my_signal(SIGSEGV, again_handler);
427 #ifdef SIGIOT
428 	my_signal(SIGIOT, again_handler);
429 #endif
430 #ifdef SIGEMT
431 	my_signal(SIGEMT, again_handler);
432 #endif
433 #ifdef SIGALRM
434 	my_signal(SIGALRM, again_handler);
435 #endif
436 	my_signal(SIGINT, again_handler);
437 }
438 
compute_block_badboy(int n)439 void compute_block_badboy(int n)
440 {
441 	int j;
442 
443 	if (malloc_flag == 1) {
444 		free(the_data);
445 		the_data = bad_malloc(n);
446 	}
447 
448 	for (j = 0; j < n; ++j) {
449 #ifdef WANT_SLOW_RAND
450 		the_data[j] = 0xFF & (int)(256.0 * rand() / (RAND_MAX + 1.0));
451 #else
452 		the_data[j] = (rand() >> 7) & 0xFF;
453 #endif
454 #ifdef __powerpc__
455 		__asm__
456 		    __volatile__("dcbst 0,%0 ; icbi 0,%0 ; isync"::"r"
457 				 (&the_data[j]));
458 #endif
459 
460 	}
461 
462 	/* was (nbytes < 0) */
463 	if (x_opt) {
464 		if (verbose_level >= 1)
465 			printf("Dump of %d bytes of data\n", n);
466 		for (j = 0; j < n; ++j) {
467 			if ((j % 16) == 0)
468 				printf("\n%04d: ", j);
469 
470 			printf("%02x ", the_data[j]);
471 		}
472 		putc('\n', stdout);
473 	}
474 }
475 
castaway(char * dat)476 BADBOY castaway(char *dat)
477 {
478 	return ((BADBOY) dat);
479 }
480 
compute_badboy(void)481 void compute_badboy(void)
482 {
483 	if (incptr == 0) {
484 		compute_block_badboy(nbytes);
485 		badboy = castaway(the_data);
486 	}
487 	/* trigger block generation at xx % of the current block */
488 	else if ((next_offset == 0)
489 		 || (next_offset > ((nbytes * BLOCK_TRIGGER) / 100))) {
490 		compute_block_badboy(nbytes);
491 		offset = 0;
492 		next_offset = offset + incptr;
493 		badboy = castaway(the_data);
494 	} else {
495 		offset = next_offset;
496 		next_offset = offset + incptr;
497 		badboy = castaway(&the_data[offset]);
498 	}
499 }
500 
try_one_crash(void)501 void try_one_crash(void)
502 {
503 	/* was (nbytes < 0) */
504 	if (!x_opt)
505 		(*badboy) ();
506 	else if (nbytes == 0)
507 		while (1) ;
508 }
509