1/* 2 * crash02.c - Test OS robustness by executing syscalls with random args. 3 * 4 * Copyright (C) 2001 Stephane Fillod <f4cfe@free.fr> 5 * 6 * This test program was inspired from crashme, by GEORGE J. CARRETT. 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * as published by the Free Software Foundation; either version 2 11 * of the License, or (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21 */ 22 23/* 24A signal handler is set up so that in most cases the machine exception 25generated by the illegal syscall, bad operands, etc in the procedure 26made up of random data are caught; and another round of randomness may 27be tried. Eventually a random syscall may corrupt the program or 28the machine state in such a way that the program must halt. This is 29a test of the robustness of the hardware/software for instruction 30fault handling. 31 32Note: Running this program just a few times, using total CPU time of 33less than a few seconds SHOULD NOT GIVE YOU ANY CONFIDENCE in system 34robustness. Having it run for hours, with tens of thousands of cases 35would be a different thing. It would also make sense to run this 36stress test at the same time you run other tests, like a multi-user 37benchmark. 38 39CAUTION: running this program may crash your system, your disk and all 40 your data along! DO NOT RUN IT ON PRODUCTION SYSTEMS! 41 CONSIDER YOUR DISK FRIED. 42 REMEMBER THE DISCLAIMER PART OF THE LICENSE. 43 44 Running as user nobody and with all your filesystems 45 remounted to readonly may be wise.. 46 47TODO: 48 * in rand_long(), stuff in some real pointers to random data 49 * Does a syscall is supposed to send SIGSEGV? 50*/ 51 52#define _GNU_SOURCE 53#include <sys/syscall.h> 54#include <stdio.h> 55#include <stdlib.h> 56#include <string.h> 57#include <signal.h> 58#include <setjmp.h> 59#include <time.h> 60#include <unistd.h> 61#include <errno.h> 62#include <sys/types.h> 63#include <sys/wait.h> 64 65#include "test.h" 66 67char *TCID = "crash02"; 68int TST_TOTAL = 1; 69 70static int x_opt = 0; 71static int v_opt = 0; 72static char *v_copt; 73static int s_opt = 0; 74static char *s_copt; 75static int l_opt = 0; 76static char *l_copt; 77static int n_opt = 0; 78static char *n_copt; 79 80int verbose_level = 2; 81 82/* depends on architecture.. */ 83unsigned int sysno_max = 127; 84 85int nseed; 86int ntries = 100; 87 88/* max time allowed per try, in seconds */ 89#define MAX_TRY_TIME 5 90 91void cleanup(void) 92{ 93 94 tst_rmdir(); 95 96} 97 98void setup(void) 99{ 100 /* 101 * setup a default signal hander and a 102 * temporary working directory. 103 */ 104 tst_sig(FORK, DEF_HANDLER, cleanup); 105 106 TEST_PAUSE; 107 108 tst_tmpdir(); 109} 110 111void help(void) 112{ 113 printf 114 (" -x dry run, hexdump random code instead\n"); 115 printf(" -l x max syscall no\n"); 116 printf(" -v x verbose level\n"); 117 printf(" -s x random seed\n"); 118 printf(" -n x ntries\n"); 119} 120 121/* 122 */ 123option_t options[] = { 124 {"v:", &v_opt, &v_copt}, 125 {"l:", &l_opt, &l_copt}, 126 {"s:", &s_opt, &s_copt}, 127 {"n:", &n_opt, &n_copt}, 128 {"x", &x_opt, NULL}, 129 130 {NULL, NULL, NULL} 131}; 132 133void badboy_fork(); 134void badboy_loop(); 135 136void summarize_errno(); 137void record_errno(unsigned int n); 138 139int main(int argc, char *argv[]) 140{ 141 int lc; 142 143 tst_parse_opts(argc, argv, options, help); 144 145 if (v_opt) 146 verbose_level = atoi(v_copt); 147 148 if (n_opt) 149 ntries = atoi(n_copt); 150 151 if (l_opt) 152 sysno_max = atoi(l_copt); 153 154 if (s_opt) 155 nseed = atoi(s_copt); 156 else 157 nseed = time(NULL); 158 159 setup(); 160 161 for (lc = 0; TEST_LOOPING(lc); lc++) { 162 tst_count = 0; 163 164 tst_resm(TINFO, "crashme02 %d %d %d", sysno_max, nseed, ntries); 165 166 srand(nseed); 167 badboy_fork(); 168 169 /* still there? */ 170 tst_resm(TPASS, "we're still here, OS seems to be robust"); 171 172 nseed++; 173 } 174 cleanup(); 175 tst_exit(); 176} 177 178/* ************************* */ 179int badboy_pid; 180 181void my_signal(int sig, void (*func) ()); 182 183void monitor_fcn(int sig) 184{ 185 int status; 186 187 if (verbose_level >= 3) 188 printf("time limit reached on pid. using kill.\n"); 189 190 status = kill(badboy_pid, SIGKILL); 191 if (status < 0) { 192 if (verbose_level >= 3) 193 printf("failed to kill process\n"); 194 } 195} 196 197void badboy_fork(void) 198{ 199 int status, pid; 200 pid_t child; 201 child = fork(); 202 badboy_pid = status; 203 switch (child) { 204 case -1: 205 perror("fork"); 206 case 0: 207#ifdef DEBUG_LATE_BADBOY 208 sleep(ntries * MAX_TRY_TIME + 10); 209#else 210 badboy_loop(); 211#endif 212 exit(0); 213 default: 214 if (verbose_level > 3) 215 printf("badboy pid = %d\n", badboy_pid); 216 217 /* don't trust the child to return at night */ 218 my_signal(SIGALRM, monitor_fcn); 219 alarm(ntries * MAX_TRY_TIME); 220 221 pid = waitpid(-1, &status, WUNTRACED); 222 if (pid <= 0) 223 perror("wait"); 224 else { 225 if (verbose_level > 3) 226 printf("pid %d exited with status %d\n", 227 pid, status); 228#if 0 229 record_status(status); 230#endif 231 } 232 } 233 alarm(0); 234} 235 236/* *************** status recording ************************* */ 237 238/* errno status table (max is actually around 127) */ 239#define STATUS_MAX 256 240static int errno_table[STATUS_MAX]; 241 242void record_errno(unsigned int n) 243{ 244 if (n >= STATUS_MAX) 245 return; 246 247 errno_table[n]++; 248} 249 250/* may not work with -c option */ 251void summarize_errno(void) 252{ 253 int i; 254 255 if (x_opt || verbose_level < 2) 256 return; 257 258 printf("errno status ... number of cases\n"); 259 for (i = 0; i < STATUS_MAX; i++) { 260 if (errno_table[i]) 261 printf("%12d ... %5d\n", i, errno_table[i]); 262 } 263} 264 265/* ************* badboy ******************************************* */ 266 267jmp_buf again_buff; 268 269unsigned char *bad_malloc(int n); 270void my_signal(int sig, void (*func) ()); 271void again_handler(int sig); 272void try_one_crash(int try_num); 273void set_up_signals(); 274int in_blacklist(int sysno); 275 276/* badboy "entry" point */ 277 278/* 279 * Unlike crashme, faulty syscalls are not supposed to barf 280 */ 281void badboy_loop(void) 282{ 283 int i; 284 285 for (i = 0; i < ntries; ++i) { 286 /* level 5 */ 287 288 if (!x_opt && verbose_level >= 5) { 289 printf("try %d\n", i); 290 } 291 292 if (setjmp(again_buff) == 3) { 293 if (verbose_level >= 5) 294 printf("Barfed\n"); 295 } else { 296 set_up_signals(); 297 alarm(MAX_TRY_TIME); 298 try_one_crash(i); 299 } 300 } 301 summarize_errno(); 302} 303 304void again_handler(int sig) 305{ 306 char *ss; 307 308 switch (sig) { 309 case SIGILL: 310 ss = " illegal instruction"; 311 break; 312#ifdef SIGTRAP 313 case SIGTRAP: 314 ss = " trace trap"; 315 break; 316#endif 317 case SIGFPE: 318 ss = " arithmetic exception"; 319 break; 320#ifdef SIGBUS 321 case SIGBUS: 322 ss = " bus error"; 323 break; 324#endif 325 case SIGSEGV: 326 ss = " segmentation violation"; 327 break; 328#ifdef SIGIOT 329 case SIGIOT: 330 ss = " IOT instruction"; 331 break; 332#endif 333#ifdef SIGEMT 334 case SIGEMT: 335 ss = " EMT instruction"; 336 break; 337#endif 338#ifdef SIGALRM 339 case SIGALRM: 340 ss = " alarm clock"; 341 break; 342#endif 343 case SIGINT: 344 ss = " interrupt"; 345 break; 346 default: 347 ss = ""; 348 } 349 if (verbose_level >= 5) 350 printf("Got signal %d%s\n", sig, ss); 351 352 longjmp(again_buff, 3); 353} 354 355void my_signal(int sig, void (*func) ()) 356{ 357 struct sigaction act; 358 359 act.sa_handler = func; 360 memset(&act.sa_mask, 0x00, sizeof(sigset_t)); 361 act.sa_flags = SA_NOMASK | SA_RESTART; 362 sigaction(sig, &act, 0); 363} 364 365void set_up_signals(void) 366{ 367 my_signal(SIGILL, again_handler); 368#ifdef SIGTRAP 369 my_signal(SIGTRAP, again_handler); 370#endif 371 my_signal(SIGFPE, again_handler); 372#ifdef SIGBUS 373 my_signal(SIGBUS, again_handler); 374#endif 375 my_signal(SIGSEGV, again_handler); 376#ifdef SIGIOT 377 my_signal(SIGIOT, again_handler); 378#endif 379#ifdef SIGEMT 380 my_signal(SIGEMT, again_handler); 381#endif 382#ifdef SIGALRM 383 my_signal(SIGALRM, again_handler); 384#endif 385 my_signal(SIGINT, again_handler); 386} 387 388/* 389 * NB: rand() (ie. RAND_MAX) might be on 31bits only! 390 * 391 * FIXME: 64-bit systems 392 * 393 * TODO: improve arg mixing (16bits and 8bits values, NULLs, etc.). 394 * big values as returned by rand() are no so interresting 395 * (except when used as pointers) because they may fall too 396 * quickly in the invalid parameter sieve. Smaller values, 397 * will be more insidious because they may refer to existing 398 * objects (pids, fd, etc.). 399 */ 400long int rand_long(void) 401{ 402 int r1, r2; 403 404 r1 = rand(); 405 r2 = rand(); 406 407 if (r1 & 0x10000L) 408 r1 = 0; 409 if (!r1 && (r2 & 0x50000L)) 410 r2 = 0; 411 else if (!r1 && (r2 & 0x20000L)) 412 r2 &= 0x00ffL; 413 414 return (long int)((r1 & 0xffffL) << 16) | (r2 & 0xffffL); 415} 416 417void try_one_crash(int try_num) 418{ 419 long int sysno, arg1, arg2, arg3, arg4, arg5, arg6, arg7; 420 421 do { 422 sysno = rand() % sysno_max; 423 } while (in_blacklist(sysno)); 424 425 arg1 = rand_long(); 426 arg2 = rand_long(); 427 arg3 = rand_long(); 428 arg4 = rand_long(); 429 arg5 = rand_long(); 430 arg6 = rand_long(); 431 arg7 = rand_long(); 432 433 if (x_opt) { 434 if (verbose_level >= 1) 435 printf("%04d: syscall(%ld, %#lx, %#lx, %#lx, %#lx, " 436 "%#lx, %#lx, %#lx)\n", 437 try_num, sysno, arg1, arg2, arg3, arg4, arg5, 438 arg6, arg7); 439 } else { 440 syscall(sysno, arg1, arg2, arg3, arg4, arg5, arg6, arg7); 441 record_errno(errno); 442 } 443} 444 445/* The following syscalls create new processes which may cause the test 446 unable to finish. */ 447int in_blacklist(int sysno) 448{ 449 int i; 450 const int list[] = { 451#if defined(__ia64__) 452 SYS_clone2, 453#else 454 /* 455 * No SYS_fork(vfork) on IA-64. Instead, it uses, 456 * clone(child_stack=0, flags=CLONE_VM|CLONE_VFORK|SIGCHLD) 457 * clone2() 458 */ 459 460 /* 461 * NOTE (garrcoop): 462 * Could not find reference to SYS_fork(vfork) on mips32 463 * with the Montavista / Octeon toolchain. Need to develop an 464 * autoconf check for this item. 465 */ 466#if defined(__NR_vfork) && __NR_vfork 467 SYS_vfork, 468#elif defined(__NR_fork) && __NR_fork 469 SYS_fork, 470#endif 471#endif /* __ia64__ */ 472#if defined(__NR_clone) && __NR_clone 473 SYS_clone, 474#endif 475 -1 476 }; 477 478 for (i = 0; list[i] != -1; i++) { 479 if (sysno == list[i]) 480 return 1; 481 } 482 483 return 0; 484} 485