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 #define _GNU_SOURCE
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 <sys/types.h>
62 #include <sys/wait.h>
63
64 #include "test.h"
65
66 char *TCID = "crash01";
67 int TST_TOTAL = 1;
68
69 static int x_opt = 0;
70 static int v_opt = 0;
71 static char *v_copt;
72 static int s_opt = 0;
73 static char *s_copt;
74 static int b_opt = 0;
75 static char *b_copt;
76 static int n_opt = 0;
77 static char *n_copt;
78
79 int verbose_level = 2;
80
81 /* Also, it may spend more time trapping and less time computing random bytes
82 * by using the smallest incptr (while not executing already tested bits).
83 */
84 int incptr = 80;
85
86 int nseed;
87 int ntries = 100;
88
89 /* compute block of nbytes at a time */
90 const int nbytes = 2000;
91
92 /* max time allowed per try, in seconds */
93 #define MAX_TRY_TIME 5
94
95 /* in % */
96 #define BLOCK_TRIGGER 80
97
cleanup(void)98 void cleanup(void)
99 {
100
101 tst_rmdir();
102
103 }
104
setup(void)105 void setup(void)
106 {
107 /*
108 * setup a default signal hander and a
109 * temporary working directory.
110 */
111 tst_sig(FORK, DEF_HANDLER, cleanup);
112
113 tst_tmpdir();
114
115 TEST_PAUSE;
116 }
117
help(void)118 void help(void)
119 {
120 printf(" -x dry run, hexdump random code instead\n");
121 printf(" -v x verbose level\n");
122 printf(" -s x random seed\n");
123 printf(" -n x ntries\n");
124 printf(" -b x inc\n");
125 }
126
127 /*
128 * crashme [+]<nbytes>[.inc] <srand> <ntries> [nsub] [verbose]"
129 *
130 * crashme <-b [+]<nbytes>[.inc]> <-s srand> <-n ntries> [-v verbose]"
131 * crashme +2000.80 666 100 1:10:30 2
132 * nsub -> -c ?
133 */
134 option_t options[] = {
135 {"v:", &v_opt, &v_copt},
136 {"s:", &s_opt, &s_copt},
137 {"n:", &n_opt, &n_copt},
138 {"b:", &b_opt, &b_copt},
139 {"x", &x_opt, NULL},
140
141 {NULL, NULL, NULL}
142 };
143
144 int malloc_flag = 1; /* to be phased out */
145
146 void badboy_fork();
147 void badboy_loop();
148 void summarize_status();
149 void record_status(unsigned int n);
150
main(int argc,char * argv[])151 int main(int argc, char *argv[])
152 {
153 int lc;
154
155 tst_parse_opts(argc, argv, options, help);
156
157 if (v_opt)
158 verbose_level = atoi(v_copt);
159
160 if (n_opt)
161 ntries = atoi(n_copt);
162
163 if (s_opt)
164 nseed = atoi(s_copt);
165 else
166 nseed = time(NULL);
167
168 if (b_opt) {
169 int inc;
170
171 inc = atoi(b_copt);
172 if (inc <= nbytes / 2)
173 incptr = inc;
174 else
175 tst_brkm(TBROK, cleanup,
176 "Invalid arg for -b (max: %u): %s", nbytes / 2,
177 b_copt);
178 }
179
180 setup();
181
182 for (lc = 0; TEST_LOOPING(lc); lc++) {
183 tst_count = 0;
184
185 tst_resm(TINFO, "crashme %s%d.%d %d %d",
186 (malloc_flag == 0) ? "" : "+", nbytes, incptr, nseed,
187 ntries);
188
189 srand(nseed);
190 badboy_fork();
191
192 /* still there? */
193 tst_resm(TPASS, "we're still here, OS seems to be robust");
194
195 nseed++;
196 }
197 summarize_status();
198 cleanup();
199 tst_exit();
200 }
201
202 /* ************************* */
203 int badboy_pid;
204
205 void my_signal(int sig, void (*func) ());
206
monitor_fcn(int sig)207 void monitor_fcn(int sig)
208 {
209 int status;
210
211 if (verbose_level >= 3)
212 printf("time limit reached on pid. using kill.\n");
213
214 status = kill(badboy_pid, SIGKILL);
215 if (status < 0) {
216 if (verbose_level >= 3)
217 printf("failed to kill process\n");
218 }
219 }
220
badboy_fork(void)221 void badboy_fork(void)
222 {
223 int status, pid;
224
225 status = fork();
226 badboy_pid = status;
227 if (status == 0) { /* badboy */
228 #ifdef DEBUG_LATE_BADBOY
229 sleep(ntries * MAX_TRY_TIME + 10);
230 #else
231 badboy_loop();
232 #endif
233 exit(0); /* good goy, he survived! */
234 } else if (status < 0)
235 perror("fork");
236 else { /* parent watching over badboy */
237
238 if (verbose_level > 3)
239 printf("badboy pid = %d\n", badboy_pid);
240
241 /* don't trust the child to return at night */
242 my_signal(SIGALRM, monitor_fcn);
243 alarm(ntries * MAX_TRY_TIME);
244
245 pid = wait(&status);
246 if (pid <= 0) {
247 perror("wait");
248 } else {
249 if (verbose_level > 3)
250 printf("pid %d exited with status %d\n", pid,
251 status);
252 record_status(status);
253 }
254 } /* parent */
255 alarm(0);
256 }
257
258 /* *************** status recording ************************* */
259
260 #define STATUS_MAX 256
261 static int status_table[STATUS_MAX];
262
record_status(unsigned int n)263 void record_status(unsigned int n)
264 {
265 if (n >= STATUS_MAX)
266 return;
267
268 status_table[n]++;
269 }
270
271 /* may not work with -c option */
summarize_status(void)272 void summarize_status(void)
273 {
274 int i;
275
276 if (verbose_level < 2)
277 return;
278
279 printf("exit status ... number of cases\n");
280 for (i = 0; i < STATUS_MAX; i++) {
281 if (status_table[i])
282 printf("%11d ... %5d\n", i, status_table[i]);
283 }
284 }
285
286 /* ************* badboy ******************************************* */
287
288 jmp_buf again_buff;
289
290 typedef void (*BADBOY) ();
291
292 BADBOY badboy;
293 char *the_data;
294
295 int offset = 0;
296 int next_offset = 0;
297
298 char *bad_malloc(int n);
299 void my_signal(int sig, void (*func) ());
300 void again_handler(int sig);
301 void compute_block_badboy(int n);
302 void compute_badboy();
303 BADBOY castaway(char *dat);
304 void try_one_crash();
305 void set_up_signals();
306
307 /* badboy "entry" point */
badboy_loop(void)308 void badboy_loop(void)
309 {
310 int i;
311
312 if (malloc_flag == 0) {
313 the_data = bad_malloc((nbytes < 0) ? -nbytes : nbytes);
314 badboy = castaway(the_data);
315 printf("Badboy at %p\n", badboy);
316 }
317
318 for (i = 0; i < ntries; ++i) {
319 compute_badboy();
320 /* level 5 */
321
322 if (!x_opt && verbose_level >= 5) {
323 if (offset)
324 printf("try %d, offset %d\n", i, offset);
325 else if (malloc_flag == 1)
326 printf("try %d, Badboy at %p\n", i, badboy);
327 else
328 printf("try %d\n", i);
329 }
330
331 if (setjmp(again_buff) == 3) {
332 if (verbose_level >= 5)
333 printf("Barfed\n");
334 } else {
335 set_up_signals();
336 alarm(MAX_TRY_TIME);
337 try_one_crash();
338 if (!x_opt && verbose_level >= 5)
339 printf("didn't barf!\n");
340 }
341 }
342 }
343
bad_malloc(int n)344 char *bad_malloc(int n)
345 {
346 char *data;
347 data = malloc(n);
348 #ifdef pyr
349 if (mprotect(((int)data / PAGSIZ) * PAGSIZ, (n / PAGSIZ + 1) * PAGSIZ,
350 PROT_READ | PROT_WRITE | PROT_EXEC))
351 perror("mprotect");
352 #endif
353 return (data);
354 }
355
again_handler(int sig)356 void again_handler(int sig)
357 {
358 char *ss;
359
360 switch (sig) {
361 case SIGILL:
362 ss = " illegal instruction";
363 break;
364 #ifdef SIGTRAP
365 case SIGTRAP:
366 ss = " trace trap";
367 break;
368 #endif
369 case SIGFPE:
370 ss = " arithmetic exception";
371 break;
372 #ifdef SIGBUS
373 case SIGBUS:
374 ss = " bus error";
375 break;
376 #endif
377 case SIGSEGV:
378 ss = " segmentation violation";
379 break;
380 #ifdef SIGIOT
381 case SIGIOT:
382 ss = " IOT instruction";
383 break;
384 #endif
385 #ifdef SIGEMT
386 case SIGEMT:
387 ss = " EMT instruction";
388 break;
389 #endif
390 #ifdef SIGALRM
391 case SIGALRM:
392 ss = " alarm clock";
393 break;
394 #endif
395 case SIGINT:
396 ss = " interrupt";
397 break;
398 default:
399 ss = "";
400 }
401 if (verbose_level >= 5)
402 printf("Got signal %d%s\n", sig, ss);
403
404 longjmp(again_buff, 3);
405 }
406
my_signal(int sig,void (* func)())407 void my_signal(int sig, void (*func) ())
408 {
409 struct sigaction act;
410
411 act.sa_handler = func;
412 memset(&act.sa_mask, 0x00, sizeof(sigset_t));
413 act.sa_flags = SA_NOMASK | SA_RESTART;
414 sigaction(sig, &act, 0);
415 }
416
set_up_signals(void)417 void set_up_signals(void)
418 {
419 my_signal(SIGILL, again_handler);
420 #ifdef SIGTRAP
421 my_signal(SIGTRAP, again_handler);
422 #endif
423 my_signal(SIGFPE, again_handler);
424 #ifdef SIGBUS
425 my_signal(SIGBUS, again_handler);
426 #endif
427 my_signal(SIGSEGV, again_handler);
428 #ifdef SIGIOT
429 my_signal(SIGIOT, again_handler);
430 #endif
431 #ifdef SIGEMT
432 my_signal(SIGEMT, again_handler);
433 #endif
434 #ifdef SIGALRM
435 my_signal(SIGALRM, again_handler);
436 #endif
437 my_signal(SIGINT, again_handler);
438 }
439
compute_block_badboy(int n)440 void compute_block_badboy(int n)
441 {
442 int j;
443
444 if (malloc_flag == 1) {
445 free(the_data);
446 the_data = bad_malloc(n);
447 }
448
449 for (j = 0; j < n; ++j) {
450 #ifdef WANT_SLOW_RAND
451 the_data[j] = 0xFF & (int)(256.0 * rand() / (RAND_MAX + 1.0));
452 #else
453 the_data[j] = (rand() >> 7) & 0xFF;
454 #endif
455 #ifdef __powerpc__
456 __asm__
457 __volatile__("dcbst 0,%0 ; icbi 0,%0 ; isync"::"r"
458 (&the_data[j]));
459 #endif
460
461 }
462
463 /* was (nbytes < 0) */
464 if (x_opt) {
465 if (verbose_level >= 1)
466 printf("Dump of %d bytes of data\n", n);
467 for (j = 0; j < n; ++j) {
468 if ((j % 16) == 0)
469 printf("\n%04d: ", j);
470
471 printf("%02x ", the_data[j]);
472 }
473 putc('\n', stdout);
474 }
475 }
476
castaway(char * dat)477 BADBOY castaway(char *dat)
478 {
479 return ((BADBOY) dat);
480 }
481
compute_badboy(void)482 void compute_badboy(void)
483 {
484 if (incptr == 0) {
485 compute_block_badboy(nbytes);
486 badboy = castaway(the_data);
487 }
488 /* trigger block generation at xx % of the current block */
489 else if ((next_offset == 0)
490 || (next_offset > ((nbytes * BLOCK_TRIGGER) / 100))) {
491 compute_block_badboy(nbytes);
492 offset = 0;
493 next_offset = offset + incptr;
494 badboy = castaway(the_data);
495 } else {
496 offset = next_offset;
497 next_offset = offset + incptr;
498 badboy = castaway(&the_data[offset]);
499 }
500 }
501
try_one_crash(void)502 void try_one_crash(void)
503 {
504 /* was (nbytes < 0) */
505 if (!x_opt)
506 (*badboy) ();
507 else if (nbytes == 0)
508 while (1) ;
509 }
510