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@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@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
cleanup(void)97 void cleanup(void)
98 {
99
100 tst_rmdir();
101
102 }
103
setup(void)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
help(void)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
main(int argc,char * argv[])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
monitor_fcn(int sig)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
badboy_fork(void)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
record_status(unsigned int n)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 */
summarize_status(void)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 */
badboy_loop(void)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
bad_malloc(int n)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
again_handler(int sig)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
my_signal(int sig,void (* func)())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
set_up_signals(void)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
compute_block_badboy(int n)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
castaway(char * dat)476 BADBOY castaway(char *dat)
477 {
478 return ((BADBOY) dat);
479 }
480
compute_badboy(void)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
try_one_crash(void)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