Home | History | Annotate | Download | only in crash
      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 (at) 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 (at) 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 
     97 void cleanup(void)
     98 {
     99 
    100 	tst_rmdir();
    101 
    102 }
    103 
    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 
    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 
    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 
    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 
    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 
    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 */
    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 */
    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 
    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 
    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 
    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 
    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 
    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 
    476 BADBOY castaway(char *dat)
    477 {
    478 	return ((BADBOY) dat);
    479 }
    480 
    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 
    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