You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
510 lines
10 KiB
510 lines
10 KiB
/*
|
|
* 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.
|
|
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#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"
|
|
|
|
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(void)
|
|
{
|
|
|
|
tst_rmdir();
|
|
|
|
}
|
|
|
|
void setup(void)
|
|
{
|
|
/*
|
|
* setup a default signal hander and a
|
|
* temporary working directory.
|
|
*/
|
|
tst_sig(FORK, DEF_HANDLER, cleanup);
|
|
|
|
tst_tmpdir();
|
|
|
|
TEST_PAUSE;
|
|
}
|
|
|
|
void help(void)
|
|
{
|
|
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[])
|
|
{
|
|
int lc;
|
|
|
|
tst_parse_opts(argc, argv, options, help);
|
|
|
|
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(void)
|
|
{
|
|
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(void)
|
|
{
|
|
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(void)
|
|
{
|
|
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 = 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(void)
|
|
{
|
|
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(void)
|
|
{
|
|
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(void)
|
|
{
|
|
/* was (nbytes < 0) */
|
|
if (!x_opt)
|
|
(*badboy) ();
|
|
else if (nbytes == 0)
|
|
while (1) ;
|
|
}
|