github.com/prysmaticlabs/prysm@v1.4.4/third_party/afl/afl-showmap.c (about) 1 /* 2 american fuzzy lop - map display utility 3 ---------------------------------------- 4 5 Written and maintained by Michal Zalewski <lcamtuf@google.com> 6 7 Copyright 2013, 2014, 2015, 2016, 2017 Google Inc. All rights reserved. 8 9 Licensed under the Apache License, Version 2.0 (the "License"); 10 you may not use this file except in compliance with the License. 11 You may obtain a copy of the License at: 12 13 http://www.apache.org/licenses/LICENSE-2.0 14 15 A very simple tool that runs the targeted binary and displays 16 the contents of the trace bitmap in a human-readable form. Useful in 17 scripts to eliminate redundant inputs and perform other checks. 18 19 Exit code is 2 if the target program crashes; 1 if it times out or 20 there is a problem executing it; or 0 if execution is successful. 21 22 */ 23 24 #define AFL_MAIN 25 26 #include "config.h" 27 #include "types.h" 28 #include "debug.h" 29 #include "alloc-inl.h" 30 #include "hash.h" 31 32 #include <stdio.h> 33 #include <unistd.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <time.h> 37 #include <errno.h> 38 #include <signal.h> 39 #include <dirent.h> 40 #include <fcntl.h> 41 42 #include <sys/wait.h> 43 #include <sys/time.h> 44 #include <sys/shm.h> 45 #include <sys/stat.h> 46 #include <sys/types.h> 47 #include <sys/resource.h> 48 49 static s32 child_pid; /* PID of the tested program */ 50 51 static u8* trace_bits; /* SHM with instrumentation bitmap */ 52 53 static u8 *out_file, /* Trace output file */ 54 *doc_path, /* Path to docs */ 55 *target_path, /* Path to target binary */ 56 *at_file; /* Substitution string for @@ */ 57 58 static u32 exec_tmout; /* Exec timeout (ms) */ 59 60 static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */ 61 62 static s32 shm_id; /* ID of the SHM region */ 63 64 static u8 quiet_mode, /* Hide non-essential messages? */ 65 edges_only, /* Ignore hit counts? */ 66 cmin_mode, /* Generate output in afl-cmin mode? */ 67 binary_mode, /* Write output as a binary map */ 68 keep_cores; /* Allow coredumps? */ 69 70 static volatile u8 71 stop_soon, /* Ctrl-C pressed? */ 72 child_timed_out, /* Child timed out? */ 73 child_crashed; /* Child crashed? */ 74 75 /* Classify tuple counts. Instead of mapping to individual bits, as in 76 afl-fuzz.c, we map to more user-friendly numbers between 1 and 8. */ 77 78 static const u8 count_class_human[256] = { 79 80 [0] = 0, 81 [1] = 1, 82 [2] = 2, 83 [3] = 3, 84 [4 ... 7] = 4, 85 [8 ... 15] = 5, 86 [16 ... 31] = 6, 87 [32 ... 127] = 7, 88 [128 ... 255] = 8 89 90 }; 91 92 static const u8 count_class_binary[256] = { 93 94 [0] = 0, 95 [1] = 1, 96 [2] = 2, 97 [3] = 4, 98 [4 ... 7] = 8, 99 [8 ... 15] = 16, 100 [16 ... 31] = 32, 101 [32 ... 127] = 64, 102 [128 ... 255] = 128 103 104 }; 105 106 static void classify_counts(u8* mem, const u8* map) { 107 108 u32 i = MAP_SIZE; 109 110 if (edges_only) { 111 112 while (i--) { 113 if (*mem) *mem = 1; 114 mem++; 115 } 116 117 } else { 118 119 while (i--) { 120 *mem = map[*mem]; 121 mem++; 122 } 123 124 } 125 126 } 127 128 129 /* Get rid of shared memory (atexit handler). */ 130 131 static void remove_shm(void) { 132 133 shmctl(shm_id, IPC_RMID, NULL); 134 135 } 136 137 138 /* Configure shared memory. */ 139 140 static void setup_shm(void) { 141 142 u8* shm_str; 143 144 shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600); 145 146 if (shm_id < 0) PFATAL("shmget() failed"); 147 148 atexit(remove_shm); 149 150 shm_str = alloc_printf("%d", shm_id); 151 152 setenv(SHM_ENV_VAR, shm_str, 1); 153 154 ck_free(shm_str); 155 156 trace_bits = shmat(shm_id, NULL, 0); 157 158 if (!trace_bits) PFATAL("shmat() failed"); 159 160 } 161 162 /* Write results. */ 163 164 static u32 write_results(void) { 165 166 s32 fd; 167 u32 i, ret = 0; 168 169 u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"), 170 caa = !!getenv("AFL_CMIN_ALLOW_ANY"); 171 172 if (!strncmp(out_file, "/dev/", 5)) { 173 174 fd = open(out_file, O_WRONLY, 0600); 175 if (fd < 0) PFATAL("Unable to open '%s'", out_file); 176 177 } else if (!strcmp(out_file, "-")) { 178 179 fd = dup(1); 180 if (fd < 0) PFATAL("Unable to open stdout"); 181 182 } else { 183 184 unlink(out_file); /* Ignore errors */ 185 fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600); 186 if (fd < 0) PFATAL("Unable to create '%s'", out_file); 187 188 } 189 190 191 if (binary_mode) { 192 193 for (i = 0; i < MAP_SIZE; i++) 194 if (trace_bits[i]) ret++; 195 196 ck_write(fd, trace_bits, MAP_SIZE, out_file); 197 close(fd); 198 199 } else { 200 201 FILE* f = fdopen(fd, "w"); 202 203 if (!f) PFATAL("fdopen() failed"); 204 205 for (i = 0; i < MAP_SIZE; i++) { 206 207 if (!trace_bits[i]) continue; 208 ret++; 209 210 if (cmin_mode) { 211 212 if (child_timed_out) break; 213 if (!caa && child_crashed != cco) break; 214 215 fprintf(f, "%u%u\n", trace_bits[i], i); 216 217 } else fprintf(f, "%06u:%u\n", i, trace_bits[i]); 218 219 } 220 221 fclose(f); 222 223 } 224 225 return ret; 226 227 } 228 229 230 /* Handle timeout signal. */ 231 232 static void handle_timeout(int sig) { 233 234 child_timed_out = 1; 235 if (child_pid > 0) kill(child_pid, SIGKILL); 236 237 } 238 239 240 /* Execute target application. */ 241 242 static void run_target(char** argv) { 243 244 static struct itimerval it; 245 int status = 0; 246 247 if (!quiet_mode) 248 SAYF("-- Program output begins --\n" cRST); 249 250 MEM_BARRIER(); 251 252 child_pid = fork(); 253 254 if (child_pid < 0) PFATAL("fork() failed"); 255 256 if (!child_pid) { 257 258 struct rlimit r; 259 260 if (quiet_mode) { 261 262 s32 fd = open("/dev/null", O_RDWR); 263 264 if (fd < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0) { 265 *(u32*)trace_bits = EXEC_FAIL_SIG; 266 PFATAL("Descriptor initialization failed"); 267 } 268 269 close(fd); 270 271 } 272 273 if (mem_limit) { 274 275 r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20; 276 277 #ifdef RLIMIT_AS 278 279 setrlimit(RLIMIT_AS, &r); /* Ignore errors */ 280 281 #else 282 283 setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ 284 285 #endif /* ^RLIMIT_AS */ 286 287 } 288 289 if (!keep_cores) r.rlim_max = r.rlim_cur = 0; 290 else r.rlim_max = r.rlim_cur = RLIM_INFINITY; 291 292 setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ 293 294 if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0); 295 296 setsid(); 297 298 execv(target_path, argv); 299 300 *(u32*)trace_bits = EXEC_FAIL_SIG; 301 exit(0); 302 303 } 304 305 /* Configure timeout, wait for child, cancel timeout. */ 306 307 if (exec_tmout) { 308 309 child_timed_out = 0; 310 it.it_value.tv_sec = (exec_tmout / 1000); 311 it.it_value.tv_usec = (exec_tmout % 1000) * 1000; 312 313 } 314 315 setitimer(ITIMER_REAL, &it, NULL); 316 317 if (waitpid(child_pid, &status, 0) <= 0) FATAL("waitpid() failed"); 318 319 child_pid = 0; 320 it.it_value.tv_sec = 0; 321 it.it_value.tv_usec = 0; 322 setitimer(ITIMER_REAL, &it, NULL); 323 324 MEM_BARRIER(); 325 326 /* Clean up bitmap, analyze exit condition, etc. */ 327 328 if (*(u32*)trace_bits == EXEC_FAIL_SIG) 329 FATAL("Unable to execute '%s'", argv[0]); 330 331 classify_counts(trace_bits, binary_mode ? 332 count_class_binary : count_class_human); 333 334 if (!quiet_mode) 335 SAYF(cRST "-- Program output ends --\n"); 336 337 if (!child_timed_out && !stop_soon && WIFSIGNALED(status)) 338 child_crashed = 1; 339 340 if (!quiet_mode) { 341 342 if (child_timed_out) 343 SAYF(cLRD "\n+++ Program timed off +++\n" cRST); 344 else if (stop_soon) 345 SAYF(cLRD "\n+++ Program aborted by user +++\n" cRST); 346 else if (child_crashed) 347 SAYF(cLRD "\n+++ Program killed by signal %u +++\n" cRST, WTERMSIG(status)); 348 349 } 350 351 352 } 353 354 355 /* Handle Ctrl-C and the like. */ 356 357 static void handle_stop_sig(int sig) { 358 359 stop_soon = 1; 360 361 if (child_pid > 0) kill(child_pid, SIGKILL); 362 363 } 364 365 366 /* Do basic preparations - persistent fds, filenames, etc. */ 367 368 static void set_up_environment(void) { 369 370 setenv("ASAN_OPTIONS", "abort_on_error=1:" 371 "detect_leaks=0:" 372 "symbolize=0:" 373 "allocator_may_return_null=1", 0); 374 375 setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" 376 "symbolize=0:" 377 "abort_on_error=1:" 378 "allocator_may_return_null=1:" 379 "msan_track_origins=0", 0); 380 381 if (getenv("AFL_PRELOAD")) { 382 setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); 383 setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); 384 } 385 386 } 387 388 389 /* Setup signal handlers, duh. */ 390 391 static void setup_signal_handlers(void) { 392 393 struct sigaction sa; 394 395 sa.sa_handler = NULL; 396 sa.sa_flags = SA_RESTART; 397 sa.sa_sigaction = NULL; 398 399 sigemptyset(&sa.sa_mask); 400 401 /* Various ways of saying "stop". */ 402 403 sa.sa_handler = handle_stop_sig; 404 sigaction(SIGHUP, &sa, NULL); 405 sigaction(SIGINT, &sa, NULL); 406 sigaction(SIGTERM, &sa, NULL); 407 408 /* Exec timeout notifications. */ 409 410 sa.sa_handler = handle_timeout; 411 sigaction(SIGALRM, &sa, NULL); 412 413 } 414 415 416 /* Detect @@ in args. */ 417 418 static void detect_file_args(char** argv) { 419 420 u32 i = 0; 421 u8* cwd = getcwd(NULL, 0); 422 423 if (!cwd) PFATAL("getcwd() failed"); 424 425 while (argv[i]) { 426 427 u8* aa_loc = strstr(argv[i], "@@"); 428 429 if (aa_loc) { 430 431 u8 *aa_subst, *n_arg; 432 433 if (!at_file) FATAL("@@ syntax is not supported by this tool."); 434 435 /* Be sure that we're always using fully-qualified paths. */ 436 437 if (at_file[0] == '/') aa_subst = at_file; 438 else aa_subst = alloc_printf("%s/%s", cwd, at_file); 439 440 /* Construct a replacement argv value. */ 441 442 *aa_loc = 0; 443 n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2); 444 argv[i] = n_arg; 445 *aa_loc = '@'; 446 447 if (at_file[0] != '/') ck_free(aa_subst); 448 449 } 450 451 i++; 452 453 } 454 455 free(cwd); /* not tracked */ 456 457 } 458 459 460 /* Show banner. */ 461 462 static void show_banner(void) { 463 464 SAYF(cCYA "afl-showmap " cBRI VERSION cRST " by <lcamtuf@google.com>\n"); 465 466 } 467 468 /* Display usage hints. */ 469 470 static void usage(u8* argv0) { 471 472 show_banner(); 473 474 SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n" 475 476 "Required parameters:\n\n" 477 478 " -o file - file to write the trace data to\n\n" 479 480 "Execution control settings:\n\n" 481 482 " -t msec - timeout for each run (none)\n" 483 " -m megs - memory limit for child process (%u MB)\n" 484 " -Q - use binary-only instrumentation (QEMU mode)\n\n" 485 486 "Other settings:\n\n" 487 488 " -q - sink program's output and don't show messages\n" 489 " -e - show edge coverage only, ignore hit counts\n" 490 " -c - allow core dumps\n\n" 491 492 "This tool displays raw tuple data captured by AFL instrumentation.\n" 493 "For additional help, consult %s/README.\n\n" cRST, 494 495 argv0, MEM_LIMIT, doc_path); 496 497 exit(1); 498 499 } 500 501 502 /* Find binary. */ 503 504 static void find_binary(u8* fname) { 505 506 u8* env_path = 0; 507 struct stat st; 508 509 if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { 510 511 target_path = ck_strdup(fname); 512 513 if (stat(target_path, &st) || !S_ISREG(st.st_mode) || 514 !(st.st_mode & 0111) || st.st_size < 4) 515 FATAL("Program '%s' not found or not executable", fname); 516 517 } else { 518 519 while (env_path) { 520 521 u8 *cur_elem, *delim = strchr(env_path, ':'); 522 523 if (delim) { 524 525 cur_elem = ck_alloc(delim - env_path + 1); 526 memcpy(cur_elem, env_path, delim - env_path); 527 delim++; 528 529 } else cur_elem = ck_strdup(env_path); 530 531 env_path = delim; 532 533 if (cur_elem[0]) 534 target_path = alloc_printf("%s/%s", cur_elem, fname); 535 else 536 target_path = ck_strdup(fname); 537 538 ck_free(cur_elem); 539 540 if (!stat(target_path, &st) && S_ISREG(st.st_mode) && 541 (st.st_mode & 0111) && st.st_size >= 4) break; 542 543 ck_free(target_path); 544 target_path = 0; 545 546 } 547 548 if (!target_path) FATAL("Program '%s' not found or not executable", fname); 549 550 } 551 552 } 553 554 555 /* Fix up argv for QEMU. */ 556 557 static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { 558 559 char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); 560 u8 *tmp, *cp, *rsl, *own_copy; 561 562 /* Workaround for a QEMU stability glitch. */ 563 564 setenv("QEMU_LOG", "nochain", 1); 565 566 memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); 567 568 new_argv[2] = target_path; 569 new_argv[1] = "--"; 570 571 /* Now we need to actually find qemu for argv[0]. */ 572 573 tmp = getenv("AFL_PATH"); 574 575 if (tmp) { 576 577 cp = alloc_printf("%s/afl-qemu-trace", tmp); 578 579 if (access(cp, X_OK)) 580 FATAL("Unable to find '%s'", tmp); 581 582 target_path = new_argv[0] = cp; 583 return new_argv; 584 585 } 586 587 own_copy = ck_strdup(own_loc); 588 rsl = strrchr(own_copy, '/'); 589 590 if (rsl) { 591 592 *rsl = 0; 593 594 cp = alloc_printf("%s/afl-qemu-trace", own_copy); 595 ck_free(own_copy); 596 597 if (!access(cp, X_OK)) { 598 599 target_path = new_argv[0] = cp; 600 return new_argv; 601 602 } 603 604 } else ck_free(own_copy); 605 606 if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { 607 608 target_path = new_argv[0] = BIN_PATH "/afl-qemu-trace"; 609 return new_argv; 610 611 } 612 613 FATAL("Unable to find 'afl-qemu-trace'."); 614 615 } 616 617 618 /* Main entry point */ 619 620 int main(int argc, char** argv) { 621 622 s32 opt; 623 u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0; 624 u32 tcnt; 625 char** use_argv; 626 627 doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; 628 629 while ((opt = getopt(argc,argv,"+o:m:t:A:eqZQbc")) > 0) 630 631 switch (opt) { 632 633 case 'o': 634 635 if (out_file) FATAL("Multiple -o options not supported"); 636 out_file = optarg; 637 break; 638 639 case 'm': { 640 641 u8 suffix = 'M'; 642 643 if (mem_limit_given) FATAL("Multiple -m options not supported"); 644 mem_limit_given = 1; 645 646 if (!strcmp(optarg, "none")) { 647 648 mem_limit = 0; 649 break; 650 651 } 652 653 if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || 654 optarg[0] == '-') FATAL("Bad syntax used for -m"); 655 656 switch (suffix) { 657 658 case 'T': mem_limit *= 1024 * 1024; break; 659 case 'G': mem_limit *= 1024; break; 660 case 'k': mem_limit /= 1024; break; 661 case 'M': break; 662 663 default: FATAL("Unsupported suffix or bad syntax for -m"); 664 665 } 666 667 if (mem_limit < 5) FATAL("Dangerously low value of -m"); 668 669 if (sizeof(rlim_t) == 4 && mem_limit > 2000) 670 FATAL("Value of -m out of range on 32-bit systems"); 671 672 } 673 674 break; 675 676 case 't': 677 678 if (timeout_given) FATAL("Multiple -t options not supported"); 679 timeout_given = 1; 680 681 if (strcmp(optarg, "none")) { 682 exec_tmout = atoi(optarg); 683 684 if (exec_tmout < 20 || optarg[0] == '-') 685 FATAL("Dangerously low value of -t"); 686 687 } 688 689 break; 690 691 case 'e': 692 693 if (edges_only) FATAL("Multiple -e options not supported"); 694 edges_only = 1; 695 break; 696 697 case 'q': 698 699 if (quiet_mode) FATAL("Multiple -q options not supported"); 700 quiet_mode = 1; 701 break; 702 703 case 'Z': 704 705 /* This is an undocumented option to write data in the syntax expected 706 by afl-cmin. Nobody else should have any use for this. */ 707 708 cmin_mode = 1; 709 quiet_mode = 1; 710 break; 711 712 case 'A': 713 714 /* Another afl-cmin specific feature. */ 715 at_file = optarg; 716 break; 717 718 case 'Q': 719 720 if (qemu_mode) FATAL("Multiple -Q options not supported"); 721 if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; 722 723 qemu_mode = 1; 724 break; 725 726 case 'b': 727 728 /* Secret undocumented mode. Writes output in raw binary format 729 similar to that dumped by afl-fuzz in <out_dir/queue/fuzz_bitmap. */ 730 731 binary_mode = 1; 732 break; 733 734 case 'c': 735 736 if (keep_cores) FATAL("Multiple -c options not supported"); 737 keep_cores = 1; 738 break; 739 740 default: 741 742 usage(argv[0]); 743 744 } 745 746 if (optind == argc || !out_file) usage(argv[0]); 747 748 setup_shm(); 749 setup_signal_handlers(); 750 751 set_up_environment(); 752 753 find_binary(argv[optind]); 754 755 if (!quiet_mode) { 756 show_banner(); 757 ACTF("Executing '%s'...\n", target_path); 758 } 759 760 detect_file_args(argv + optind); 761 762 if (qemu_mode) 763 use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); 764 else 765 use_argv = argv + optind; 766 767 run_target(use_argv); 768 769 tcnt = write_results(); 770 771 if (!quiet_mode) { 772 773 if (!tcnt) FATAL("No instrumentation detected" cRST); 774 OKF("Captured %u tuples in '%s'." cRST, tcnt, out_file); 775 776 } 777 778 exit(child_crashed * 2 + child_timed_out); 779 780 }