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