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