afl-gcc.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /*
  2. american fuzzy lop - wrapper for GCC and clang
  3. ----------------------------------------------
  4. Written and maintained by Michal Zalewski <lcamtuf@google.com>
  5. Copyright 2013, 2014, 2015 Google Inc. All rights reserved.
  6. Licensed under the Apache License, Version 2.0 (the "License");
  7. you may not use this file except in compliance with the License.
  8. You may obtain a copy of the License at:
  9. http://www.apache.org/licenses/LICENSE-2.0
  10. This program is a drop-in replacement for GCC or clang. The most common way
  11. of using it is to pass the path to afl-gcc or afl-clang via CC when invoking
  12. ./configure.
  13. (Of course, use CXX and point it to afl-g++ / afl-clang++ for C++ code.)
  14. The wrapper needs to know the path to afl-as (renamed to 'as'). The default
  15. is /usr/local/lib/afl/. A convenient way to specify alternative directories
  16. would be to set AFL_PATH.
  17. If AFL_HARDEN is set, the wrapper will compile the target app with various
  18. hardening options that may help detect memory management issues more
  19. reliably. You can also specify AFL_USE_ASAN to enable ASAN.
  20. If you want to call a non-default compiler as a next step of the chain,
  21. specify its location via AFL_CC or AFL_CXX.
  22. */
  23. #define AFL_MAIN
  24. #include "config.h"
  25. #include "types.h"
  26. #include "debug.h"
  27. #include "alloc-inl.h"
  28. #include <stdio.h>
  29. #include <unistd.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. static u8* as_path; /* Path to the AFL 'as' wrapper */
  33. static u8** cc_params; /* Parameters passed to the real CC */
  34. static u32 cc_par_cnt = 1; /* Param count, including argv0 */
  35. static u8 be_quiet, /* Quiet mode */
  36. clang_mode; /* Invoked as afl-clang*? */
  37. /* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived
  38. from argv[0]. If that fails, abort. */
  39. static void find_as(u8* argv0) {
  40. u8 *afl_path = getenv("AFL_PATH");
  41. u8 *slash, *tmp;
  42. if (afl_path) {
  43. tmp = alloc_printf("%s/as", afl_path);
  44. if (!access(tmp, X_OK)) {
  45. as_path = afl_path;
  46. ck_free(tmp);
  47. return;
  48. }
  49. ck_free(tmp);
  50. }
  51. slash = strrchr(argv0, '/');
  52. if (slash) {
  53. u8 *dir;
  54. *slash = 0;
  55. dir = ck_strdup(argv0);
  56. *slash = '/';
  57. tmp = alloc_printf("%s/afl-as", dir);
  58. if (!access(tmp, X_OK)) {
  59. as_path = dir;
  60. ck_free(tmp);
  61. return;
  62. }
  63. ck_free(tmp);
  64. ck_free(dir);
  65. }
  66. if (!access(AFL_PATH "/as", X_OK)) {
  67. as_path = AFL_PATH;
  68. return;
  69. }
  70. FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH");
  71. }
  72. /* Copy argv to cc_params, making the necessary edits. */
  73. static void edit_params(u32 argc, char** argv) {
  74. u8 fortify_set = 0, asan_set = 0;
  75. u8 *name;
  76. #if defined(__FreeBSD__) && defined(__x86_64__)
  77. u8 m32_set = 0;
  78. #endif
  79. cc_params = ck_alloc((argc + 128) * sizeof(u8*));
  80. name = strrchr(argv[0], '/');
  81. if (!name) name = argv[0]; else name++;
  82. if (!strncmp(name, "afl-clang", 9)) {
  83. clang_mode = 1;
  84. setenv(CLANG_ENV_VAR, "1", 1);
  85. if (!strcmp(name, "afl-clang++")) {
  86. u8* alt_cxx = getenv("AFL_CXX");
  87. cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";
  88. } else {
  89. u8* alt_cc = getenv("AFL_CC");
  90. cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";
  91. }
  92. } else {
  93. /* With GCJ and Eclipse installed, you can actually compile Java! The
  94. instrumentation will work (amazingly). Alas, unhandled exceptions do
  95. not call abort(), so afl-fuzz would need to be modified to equate
  96. non-zero exit codes with crash conditions when working with Java
  97. binaries. Meh. */
  98. #ifdef __APPLE__
  99. if (!strcmp(name, "afl-g++")) cc_params[0] = getenv("AFL_CXX");
  100. else if (!strcmp(name, "afl-gcj")) cc_params[0] = getenv("AFL_GCJ");
  101. else cc_params[0] = getenv("AFL_CC");
  102. if (!cc_params[0]) {
  103. SAYF("\n" cLRD "[-] " cRST
  104. "On Apple systems, 'gcc' is usually just a wrapper for clang. Please use the\n"
  105. " 'afl-clang' utility instead of 'afl-gcc'. If you really have GCC installed,\n"
  106. " set AFL_CC or AFL_CXX to specify the correct path to that compiler.\n");
  107. FATAL("AFL_CC or AFL_CXX required on MacOS X");
  108. }
  109. #else
  110. if (!strcmp(name, "afl-g++")) {
  111. u8* alt_cxx = getenv("AFL_CXX");
  112. cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
  113. } else if (!strcmp(name, "afl-gcj")) {
  114. u8* alt_cc = getenv("AFL_GCJ");
  115. cc_params[0] = alt_cc ? alt_cc : (u8*)"gcj";
  116. } else {
  117. u8* alt_cc = getenv("AFL_CC");
  118. cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc";
  119. }
  120. #endif /* __APPLE__ */
  121. }
  122. while (--argc) {
  123. u8* cur = *(++argv);
  124. if (!strncmp(cur, "-B", 2)) {
  125. if (!be_quiet) WARNF("-B is already set, overriding");
  126. if (!cur[2] && argc > 1) { argc--; argv++; }
  127. continue;
  128. }
  129. if (!strcmp(cur, "-integrated-as")) continue;
  130. if (!strcmp(cur, "-pipe")) continue;
  131. #if defined(__FreeBSD__) && defined(__x86_64__)
  132. if (!strcmp(cur, "-m32")) m32_set = 1;
  133. #endif
  134. if (!strcmp(cur, "-fsanitize=address") ||
  135. !strcmp(cur, "-fsanitize=memory")) asan_set = 1;
  136. if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
  137. cc_params[cc_par_cnt++] = cur;
  138. }
  139. cc_params[cc_par_cnt++] = "-B";
  140. cc_params[cc_par_cnt++] = as_path;
  141. if (clang_mode)
  142. cc_params[cc_par_cnt++] = "-no-integrated-as";
  143. if (getenv("AFL_HARDEN")) {
  144. cc_params[cc_par_cnt++] = "-fstack-protector-all";
  145. if (!fortify_set)
  146. cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
  147. }
  148. if (asan_set) {
  149. /* Pass this on to afl-as to adjust map density. */
  150. setenv("AFL_USE_ASAN", "1", 1);
  151. } else if (getenv("AFL_USE_ASAN")) {
  152. if (getenv("AFL_USE_MSAN"))
  153. FATAL("ASAN and MSAN are mutually exclusive");
  154. if (getenv("AFL_HARDEN"))
  155. FATAL("ASAN and AFL_HARDEN are mutually exclusive");
  156. cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
  157. cc_params[cc_par_cnt++] = "-fsanitize=address";
  158. } else if (getenv("AFL_USE_MSAN")) {
  159. if (getenv("AFL_USE_ASAN"))
  160. FATAL("ASAN and MSAN are mutually exclusive");
  161. if (getenv("AFL_HARDEN"))
  162. FATAL("MSAN and AFL_HARDEN are mutually exclusive");
  163. cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
  164. cc_params[cc_par_cnt++] = "-fsanitize=memory";
  165. }
  166. if (!getenv("AFL_DONT_OPTIMIZE")) {
  167. #if defined(__FreeBSD__) && defined(__x86_64__)
  168. /* On 64-bit FreeBSD systems, clang -g -m32 is broken, but -m32 itself
  169. works OK. This has nothing to do with us, but let's avoid triggering
  170. that bug. */
  171. if (!clang_mode || !m32_set)
  172. cc_params[cc_par_cnt++] = "-g";
  173. #else
  174. cc_params[cc_par_cnt++] = "-g";
  175. #endif
  176. cc_params[cc_par_cnt++] = "-O3";
  177. cc_params[cc_par_cnt++] = "-funroll-loops";
  178. /* Two indicators that you're building for fuzzing; one of them is
  179. AFL-specific, the other is shared with libfuzzer. */
  180. cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
  181. cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
  182. }
  183. if (getenv("AFL_NO_BUILTIN")) {
  184. cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
  185. cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
  186. cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp";
  187. cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
  188. cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
  189. cc_params[cc_par_cnt++] = "-fno-builtin-strstr";
  190. cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr";
  191. }
  192. cc_params[cc_par_cnt] = NULL;
  193. }
  194. /* Main entry point */
  195. int main(int argc, char** argv) {
  196. if (isatty(2) && !getenv("AFL_QUIET")) {
  197. SAYF(cCYA "afl-cc " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
  198. } else be_quiet = 1;
  199. if (argc < 2) {
  200. SAYF("\n"
  201. "This is a helper application for afl-fuzz. It serves as a drop-in replacement\n"
  202. "for gcc or clang, letting you recompile third-party code with the required\n"
  203. "runtime instrumentation. A common use pattern would be one of the following:\n\n"
  204. " CC=%s/afl-gcc ./configure\n"
  205. " CXX=%s/afl-g++ ./configure\n\n"
  206. "You can specify custom next-stage toolchain via AFL_CC, AFL_CXX, and AFL_AS.\n"
  207. "Setting AFL_HARDEN enables hardening optimizations in the compiled code.\n\n",
  208. BIN_PATH, BIN_PATH);
  209. exit(1);
  210. }
  211. find_as(argv[0]);
  212. edit_params(argc, argv);
  213. execvp(cc_params[0], (char**)cc_params);
  214. FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
  215. return 0;
  216. }