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 /*
24 A signal handler is set up so that in most cases the machine exception
25 generated by the illegal syscall, bad operands, etc in the procedure
26 made up of random data are caught; and another round of randomness may
27 be tried. Eventually a random syscall may corrupt the program or
28 the machine state in such a way that the program must halt. This is
29 a test of the robustness of the hardware/software for instruction
30 fault handling.
31
32 Note: Running this program just a few times, using total CPU time of
33 less than a few seconds SHOULD NOT GIVE YOU ANY CONFIDENCE in system
34 robustness. Having it run for hours, with tens of thousands of cases
35 would be a different thing. It would also make sense to run this
36 stress test at the same time you run other tests, like a multi-user
37 benchmark.
38
39 CAUTION: 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
47 TODO:
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
67 char *TCID = "crash02";
68 int TST_TOTAL = 1;
69
70 static int x_opt = 0;
71 static int v_opt = 0;
72 static char *v_copt;
73 static int s_opt = 0;
74 static char *s_copt;
75 static int l_opt = 0;
76 static char *l_copt;
77 static int n_opt = 0;
78 static char *n_copt;
79
80 int verbose_level = 2;
81
82 /* depends on architecture.. */
83 unsigned int sysno_max = 127;
84
85 int nseed;
86 int ntries = 100;
87
88 /* max time allowed per try, in seconds */
89 #define MAX_TRY_TIME 5
90
cleanup(void)91 void cleanup(void)
92 {
93
94 tst_rmdir();
95
96 }
97
setup(void)98 void 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
help(void)111 void 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 */
123 option_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
133 void badboy_fork();
134 void badboy_loop();
135
136 void summarize_errno();
137 void record_errno(unsigned int n);
138
main(int argc,char * argv[])139 int 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 /* ************************* */
179 int badboy_pid;
180
181 void my_signal(int sig, void (*func) ());
182
monitor_fcn(int sig)183 void 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
badboy_fork(void)197 void badboy_fork(void)
198 {
199 int status, pid;
200 pid_t child = fork();
201
202 switch (child) {
203 case -1:
204 perror("fork");
205 case 0:
206 #ifdef DEBUG_LATE_BADBOY
207 sleep(ntries * MAX_TRY_TIME + 10);
208 #else
209 badboy_loop();
210 #endif
211 exit(0);
212 default:
213 badboy_pid = child;
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
240 static int errno_table[STATUS_MAX];
241
record_errno(unsigned int n)242 void 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 */
summarize_errno(void)251 void 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
267 jmp_buf again_buff;
268
269 unsigned char *bad_malloc(int n);
270 void my_signal(int sig, void (*func) ());
271 void again_handler(int sig);
272 void try_one_crash(int try_num);
273 void set_up_signals();
274 int in_blacklist(int sysno);
275
276 /* badboy "entry" point */
277
278 /*
279 * Unlike crashme, faulty syscalls are not supposed to barf
280 */
badboy_loop(void)281 void 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
again_handler(int sig)304 void 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
my_signal(int sig,void (* func)())355 void 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
set_up_signals(void)365 void 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 */
rand_long(void)400 long 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
try_one_crash(int try_num)417 void 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 || verbose_level >= 1)
434 printf("%04d: syscall(%ld, %#lx, %#lx, %#lx, %#lx, %#lx, "
435 "%#lx, %#lx)\n", try_num, sysno, arg1, arg2, arg3,
436 arg4, arg5, arg6, arg7);
437
438 if (!x_opt) {
439 syscall(sysno, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
440 record_errno(errno);
441 }
442 }
443
444 /* The following syscalls create new processes which may cause the test
445 unable to finish. */
in_blacklist(int sysno)446 int in_blacklist(int sysno)
447 {
448 int i;
449 const int list[] = {
450 #if defined(__ia64__)
451 SYS_clone2,
452 #else
453 /*
454 * No SYS_fork(vfork) on IA-64. Instead, it uses,
455 * clone(child_stack=0, flags=CLONE_VM|CLONE_VFORK|SIGCHLD)
456 * clone2()
457 */
458
459 /*
460 * NOTE (garrcoop):
461 * Could not find reference to SYS_fork(vfork) on mips32
462 * with the Montavista / Octeon toolchain. Need to develop an
463 * autoconf check for this item.
464 */
465 #if defined(__NR_vfork) && __NR_vfork
466 SYS_vfork,
467 #endif
468 #if 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 #if defined(__NR_vhangup) && __NR_vhangup
476 __NR_vhangup, /* int vhangup(void); - terminal logout */
477 #endif
478 #if defined(__NR_pause) && __NR_pause
479 __NR_pause, /* int pause(void); - sleep indefinitely */
480 #endif
481 #if defined(__NR_read) && __NR_read
482 /*
483 * ssize_t read(int fd, void *buf, size_t count); - will sleep
484 * indefinitely if the first argument is 0
485 */
486 __NR_read,
487 #endif
488 -1
489 };
490
491 for (i = 0; list[i] != -1; i++) {
492 if (sysno == list[i])
493 return 1;
494 }
495
496 return 0;
497 }
498