| /* |
| * crash01.c - Test OS robustness by creating a string of random bytes and then jump to it. |
| * |
| * New version Copyright (C) 2001 Stephane Fillod <f4cfe@free.fr> |
| * |
| * Original idea (c) 1990-1994 by GEORGE J. CARRETTE, CONCORD, MASSACHUSETTS. |
| * from crashme version: "2.4 20-MAY-1994" <GJC@WORLD.STD.COM> |
| */ |
| /* TODO: trapme: forge syscall with random args, and run it!! --SF */ |
| |
| /* |
| * COPYRIGHT (c) 1990-1994 BY * |
| * GEORGE J. CARRETTE, CONCORD, MASSACHUSETTS. * |
| * ALL RIGHTS RESERVED * |
| |
| Permission to use, copy, modify, distribute and sell this software |
| and its documentation for any purpose and without fee is hereby |
| granted, provided that the above copyright notice appear in all copies |
| and that both that copyright notice and this permission notice appear |
| in supporting documentation, and that the name of the author |
| not be used in advertising or publicity pertaining to distribution |
| of the software without specific, written prior permission. |
| |
| THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING |
| ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL |
| HE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR |
| ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
| WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
| ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
| SOFTWARE. |
| |
| */ |
| |
| /* |
| |
| A signal handler is set up so that in most cases the machine exception |
| generated by the illegal instructions, bad operands, etc in the procedure |
| made up of random data are caught; and another round of randomness may |
| be tried. Eventually a random instruction may corrupt the program or |
| the machine state in such a way that the program must halt. This is |
| a test of the robustness of the hardware/software for instruction |
| fault handling. |
| |
| Note: Running this program just a few times, using total CPU time of |
| less than a few seconds SHOULD NOT GIVE YOU ANY CONFIDENCE in system |
| robustness. Having it run for hours, with tens of thousands of cases |
| would be a different thing. It would also make sense to run this |
| stress test at the same time you run other tests, like a multi-user |
| benchmark. |
| |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <setjmp.h> |
| #include <time.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| |
| #include "test.h" |
| #include "usctest.h" |
| |
| char *TCID="crash01"; |
| int TST_TOTAL=1; |
| |
| static int x_opt = 0; |
| static int v_opt = 0; |
| static char *v_copt; |
| static int s_opt = 0; |
| static char *s_copt; |
| static int b_opt = 0; |
| static char *b_copt; |
| static int n_opt = 0; |
| static char *n_copt; |
| |
| int verbose_level = 2; |
| |
| /* Also, it may spend more time trapping and less time computing random bytes |
| * by using the smallest incptr (while not executing already tested bits). |
| */ |
| int incptr = 80; |
| |
| int nseed; |
| int ntries = 100; |
| |
| /* compute block of nbytes at a time */ |
| const int nbytes = 2000; |
| |
| /* max time allowed per try, in seconds */ |
| #define MAX_TRY_TIME 5 |
| |
| /* in % */ |
| #define BLOCK_TRIGGER 80 |
| |
| void cleanup() |
| { |
| /* |
| * remove the tmp directory and exit |
| */ |
| |
| TEST_CLEANUP; |
| |
| tst_rmdir(); |
| |
| } |
| |
| void setup() |
| { |
| /* |
| * setup a default signal hander and a |
| * temporary working directory. |
| */ |
| tst_sig(FORK, DEF_HANDLER, cleanup); |
| |
| tst_tmpdir(); |
| |
| TEST_PAUSE; |
| } |
| |
| void help() |
| { |
| printf(" -x dry run, hexdump random code instead\n"); |
| printf(" -v x verbose level\n"); |
| printf(" -s x random seed\n"); |
| printf(" -n x ntries\n"); |
| printf(" -b x inc\n"); |
| } |
| |
| /* |
| * crashme [+]<nbytes>[.inc] <srand> <ntries> [nsub] [verbose]" |
| * |
| * crashme <-b [+]<nbytes>[.inc]> <-s srand> <-n ntries> [-v verbose]" |
| * crashme +2000.80 666 100 1:10:30 2 |
| * nsub -> -c ? |
| */ |
| option_t options[] = |
| { |
| { "v:", &v_opt, &v_copt }, |
| { "s:", &s_opt, &s_copt }, |
| { "n:", &n_opt, &n_copt }, |
| { "b:", &b_opt, &b_copt }, |
| { "x", &x_opt, NULL }, |
| |
| { NULL, NULL, NULL } |
| }; |
| |
| int malloc_flag = 1; /* to be phased out */ |
| |
| void badboy_fork (); |
| void badboy_loop (); |
| void summarize_status (); |
| void record_status(unsigned int n); |
| |
| int |
| main (int argc, char *argv[]) |
| { |
| char *msg; |
| int lc; |
| |
| if ((msg=parse_opts(argc, argv, options, help)) != (char *) NULL) |
| tst_brkm(TBROK, cleanup, "OPTION PARSING ERROR - %s", msg); |
| |
| if (v_opt) |
| verbose_level = atoi(v_copt); |
| |
| if (n_opt) |
| ntries = atoi(n_copt); |
| |
| if (s_opt) |
| nseed = atoi(s_copt); |
| else |
| nseed = time(NULL); |
| |
| if (b_opt) { |
| int inc; |
| |
| inc = atoi(b_copt); |
| if (inc <= nbytes/2) |
| incptr = inc; |
| else |
| tst_brkm(TBROK, cleanup, "Invalid arg for -b (max: %u): %s", nbytes/2, b_copt); |
| } |
| |
| setup(); |
| |
| for (lc=0; TEST_LOOPING(lc); lc++) |
| { |
| Tst_count=0; |
| |
| tst_resm(TINFO, "crashme %s%d.%d %d %d", |
| (malloc_flag == 0) ? "" : "+", nbytes, incptr, nseed, ntries); |
| |
| srand (nseed); |
| badboy_fork (); |
| |
| /* still there? */ |
| tst_resm(TPASS, "we're still here, OS seems to be robust"); |
| |
| nseed++; |
| } |
| summarize_status(); |
| cleanup(); |
| tst_exit(); |
| } |
| |
| /* ************************* */ |
| int badboy_pid; |
| |
| void my_signal (int sig, void (*func) ()); |
| |
| void monitor_fcn (int sig) |
| { |
| int status; |
| |
| if (verbose_level >= 3) |
| printf ("time limit reached on pid. using kill.\n"); |
| |
| status = kill (badboy_pid, SIGKILL); |
| if (status < 0) |
| { |
| if (verbose_level >= 3) |
| printf ("failed to kill process\n"); |
| } |
| } |
| |
| void |
| badboy_fork () |
| { |
| int status, pid; |
| |
| status = fork (); |
| badboy_pid = status; |
| if (status == 0) /* badboy */ |
| { |
| #ifdef DEBUG_LATE_BADBOY |
| sleep(ntries*MAX_TRY_TIME+10); |
| #else |
| badboy_loop (); |
| #endif |
| exit (0); /* good goy, he survived! */ |
| } |
| else if (status < 0) |
| perror ("fork"); |
| else /* parent watching over badboy */ |
| { |
| if (verbose_level > 3) |
| printf ("badboy pid = %d\n", badboy_pid); |
| |
| /* don't trust the child to return at night */ |
| my_signal (SIGALRM, monitor_fcn); |
| alarm (ntries*MAX_TRY_TIME); |
| |
| pid = wait (&status); |
| if (pid <= 0) |
| { |
| perror ("wait"); |
| } else { |
| if (verbose_level > 3) |
| printf ("pid %d exited with status %d\n", pid, status); |
| record_status(status); |
| } |
| } /* parent */ |
| alarm(0); |
| } |
| |
| /* *************** status recording ************************* */ |
| |
| #define STATUS_MAX 256 |
| static int status_table[STATUS_MAX]; |
| |
| void record_status(unsigned int n) |
| { |
| if (n >= STATUS_MAX) |
| return; |
| |
| status_table[n]++; |
| } |
| |
| /* may not work with -c option */ |
| void |
| summarize_status () |
| { |
| int i; |
| |
| if (verbose_level < 2) |
| return; |
| |
| printf ("exit status ... number of cases\n"); |
| for (i = 0; i < STATUS_MAX; i++) |
| { |
| if (status_table[i]) |
| printf ( "%11d ... %5d\n", i, status_table[i]); |
| } |
| } |
| |
| /* ************* badboy ******************************************* */ |
| |
| jmp_buf again_buff; |
| |
| typedef void (*BADBOY) (); |
| |
| BADBOY badboy; |
| char *the_data; |
| |
| int offset = 0; |
| int next_offset = 0; |
| |
| char * bad_malloc (int n); |
| void my_signal (int sig, void (*func) ()); |
| void again_handler (int sig); |
| void compute_block_badboy (int n); |
| void compute_badboy (); |
| BADBOY castaway (char *dat); |
| void try_one_crash (); |
| void set_up_signals (); |
| |
| /* badboy "entry" point */ |
| void |
| badboy_loop () |
| { |
| int i; |
| |
| if (malloc_flag == 0) |
| { |
| the_data = bad_malloc ((nbytes < 0) ? -nbytes : nbytes); |
| badboy = castaway (the_data); |
| printf ("Badboy at %p\n", badboy); |
| } |
| |
| for (i = 0; i < ntries; ++i) |
| { |
| compute_badboy (); |
| /* level 5 */ |
| |
| if (!x_opt && verbose_level >= 5) { |
| if (offset) |
| printf ("try %d, offset %d\n", i, offset); |
| else if (malloc_flag == 1) |
| printf ("try %d, Badboy at %p\n", i, badboy); |
| else |
| printf ("try %d\n", i); |
| } |
| |
| if (setjmp (again_buff) == 3) |
| { |
| if (verbose_level >= 5) |
| printf ("Barfed\n"); |
| } |
| else |
| { |
| set_up_signals (); |
| alarm (MAX_TRY_TIME); |
| try_one_crash (); |
| if (!x_opt && verbose_level >= 5) |
| printf ("didn't barf!\n"); |
| } |
| } |
| } |
| |
| char * bad_malloc (int n) |
| { |
| char *data; |
| data = (char *) malloc (n); |
| #ifdef pyr |
| if (mprotect (((int) data / PAGSIZ) * PAGSIZ, (n / PAGSIZ + 1) * PAGSIZ, |
| PROT_READ | PROT_WRITE | PROT_EXEC)) |
| perror ("mprotect"); |
| #endif |
| return (data); |
| } |
| |
| void again_handler (int sig) |
| { |
| char *ss; |
| |
| switch (sig) |
| { |
| case SIGILL: |
| ss = " illegal instruction"; |
| break; |
| #ifdef SIGTRAP |
| case SIGTRAP: |
| ss = " trace trap"; |
| break; |
| #endif |
| case SIGFPE: |
| ss = " arithmetic exception"; |
| break; |
| #ifdef SIGBUS |
| case SIGBUS: |
| ss = " bus error"; |
| break; |
| #endif |
| case SIGSEGV: |
| ss = " segmentation violation"; |
| break; |
| #ifdef SIGIOT |
| case SIGIOT: |
| ss = " IOT instruction"; |
| break; |
| #endif |
| #ifdef SIGEMT |
| case SIGEMT: |
| ss = " EMT instruction"; |
| break; |
| #endif |
| #ifdef SIGALRM |
| case SIGALRM: |
| ss = " alarm clock"; |
| break; |
| #endif |
| case SIGINT: |
| ss = " interrupt"; |
| break; |
| default: |
| ss = ""; |
| } |
| if (verbose_level >= 5) |
| printf ("Got signal %d%s\n", sig, ss); |
| |
| longjmp (again_buff, 3); |
| } |
| |
| void my_signal (int sig, void (*func) ()) |
| { |
| struct sigaction act; |
| |
| act.sa_handler = func; |
| memset (&act.sa_mask, 0x00, sizeof (sigset_t)); |
| act.sa_flags = SA_NOMASK|SA_RESTART; |
| sigaction (sig, &act, 0); |
| } |
| |
| void |
| set_up_signals () |
| { |
| my_signal (SIGILL, again_handler); |
| #ifdef SIGTRAP |
| my_signal (SIGTRAP, again_handler); |
| #endif |
| my_signal (SIGFPE, again_handler); |
| #ifdef SIGBUS |
| my_signal (SIGBUS, again_handler); |
| #endif |
| my_signal (SIGSEGV, again_handler); |
| #ifdef SIGIOT |
| my_signal (SIGIOT, again_handler); |
| #endif |
| #ifdef SIGEMT |
| my_signal (SIGEMT, again_handler); |
| #endif |
| #ifdef SIGALRM |
| my_signal (SIGALRM, again_handler); |
| #endif |
| my_signal (SIGINT, again_handler); |
| } |
| |
| void compute_block_badboy (int n) |
| { |
| int j; |
| |
| if (malloc_flag == 1) { |
| free(the_data); |
| the_data = bad_malloc (n); |
| } |
| |
| for (j = 0; j < n; ++j) { |
| #ifdef WANT_SLOW_RAND |
| the_data[j] = 0xFF & (int) (256.0*rand()/(RAND_MAX+1.0)); |
| #else |
| the_data[j] = (rand () >> 7) & 0xFF; |
| #endif |
| #ifdef __powerpc__ |
| __asm__ __volatile__("dcbst 0,%0 ; icbi 0,%0 ; isync" : : "r"(&the_data[j]) ); |
| #endif |
| |
| } |
| |
| /* was (nbytes < 0) */ |
| if (x_opt) |
| { |
| if (verbose_level >= 1) |
| printf ("Dump of %d bytes of data\n", n); |
| for (j = 0; j < n; ++j) |
| { |
| if ((j % 16) == 0) |
| printf ("\n%04d: ", j); |
| |
| printf ("%02x ", the_data[j]); |
| } |
| putc ('\n', stdout); |
| } |
| } |
| |
| BADBOY |
| castaway (char *dat) |
| { |
| return ((BADBOY) dat); |
| } |
| |
| void |
| compute_badboy () |
| { |
| if (incptr == 0) |
| { |
| compute_block_badboy (nbytes); |
| badboy = castaway (the_data); |
| } |
| /* trigger block generation at xx % of the current block */ |
| else if ((next_offset == 0) || (next_offset > ((nbytes * BLOCK_TRIGGER) / 100))) |
| { |
| compute_block_badboy (nbytes); |
| offset = 0; |
| next_offset = offset + incptr; |
| badboy = castaway (the_data); |
| } |
| else |
| { |
| offset = next_offset; |
| next_offset = offset + incptr; |
| badboy = castaway (&the_data[offset]); |
| } |
| } |
| |
| void |
| try_one_crash () |
| { |
| /* was (nbytes < 0) */ |
| if (!x_opt) |
| (*badboy) (); |
| else if (nbytes == 0) |
| while (1); |
| } |