//该函数处于AFL/llvm_mode/afl-llvm-rt.o.c /* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard. It remains non-operational in the traditional, plugin-backed LLVM mode. For more info about 'trace-pc-guard', see README.llvm. The first function (__sanitizer_cov_trace_pc_guard) is called back on every edge (as opposed to every basic block). */
/* Init callback. Populates instrumentation IDs. Note that we're using ID of 0 as a special value to indicate non-instrumented bits. That may still touch the bitmap, but in a fairly harmless way. */
x = getenv("AFL_INST_RATIO"); if (x) inst_ratio = atoi(x);
if (!inst_ratio || inst_ratio > 100) { fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100).\n"); abort(); }
/* Make sure that the first element in the range is always set - we use that to avoid duplicate calls (which can happen as an artifact of the underlying implementation in LLVM). */
/* Write modified data to file for testing. If out_file is set, the old file is unlinked and a new one is created. Otherwise, out_fd is rewound and truncated. */
Address Sanitizer又名ASan,是一个快速的C/C++内存错误检查器。它可以检测到:UAF、Heap buffer overflow、Stack buffer overflow、Global buffer overflow、Use after return、Use after scpoe、Initialization order bugs、Memory leak等漏洞类型。asan可以简单理解成对malloc和free以及存取指令等的hook,从而在发现分配出来的内存的大小,小于要存取的index的大小时,检测出越界读写问题。
Address Sanitizer又名ASan,是一个快速的C/C++内存错误检查器。它可以检测到:UAF、Heap buffer overflow、Stack buffer overflow、Global buffer overflow、Use after return、Use after scpoe、Initialization order bugs、Memory leak等漏洞类型。asan可以简单理解成对malloc和free以及存取指令等的hook,从而在发现分配出来的内存的大小,小于要存取的index的大小时,检测出越界读写问题。
if (isatty(2) && !getenv("AFL_QUIET")) { //检查stderr是否连接/dev/
SAYF(cCYA "afl-cc " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
} else be_quiet = 1;
if (argc < 2) {
SAYF("\n" "This is a helper application for afl-fuzz. It serves as a drop-in replacement\n" "for gcc or clang, letting you recompile third-party code with the required\n" "runtime instrumentation. A common use pattern would be one of the following:\n\n"
"You can specify custom next-stage toolchain via AFL_CC, AFL_CXX, and AFL_AS.\n" "Setting AFL_HARDEN enables hardening optimizations in the compiled code.\n\n", BIN_PATH, BIN_PATH);
exit(1);
}
find_as(argv[0]);
edit_params(argc, argv);
execvp(cc_params[0], (char**)cc_params);
FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
/* In non-debug mode, we just do straightforward aliasing of the above functions to user-visible names such as ck_alloc(). */ /* 在非调试模式下,我们只需将上述函数直接别名为用户可见的名称,例如 ck_alloc() */
} --------------------------------------------------------------------------------------------------- /* Magic tokens used to mark used / freed chunks. */
#define ALLOC_MAGIC_C1 0xFF00FF00 /* Used head (dword) */ #define ALLOC_MAGIC_F 0xFE00FE00 /* Freed head (dword) */ #define ALLOC_MAGIC_C2 0xF0 /* Used tail (byte) */
/* Positions of guard tokens in relation to the user-visible pointer. */
/* With GCJ and Eclipse installed, you can actually compile Java! The instrumentation will work (amazingly). Alas, unhandled exceptions do not call abort(), so afl-fuzz would need to be modified to equate non-zero exit codes with crash conditions when working with Java binaries. Meh. */
SAYF("\n" cLRD "[-] " cRST "On Apple systems, 'gcc' is usually just a wrapper for clang. Please use the\n" " 'afl-clang' utility instead of 'afl-gcc'. If you really have GCC installed,\n" " set AFL_CC or AFL_CXX to specify the correct path to that compiler.\n");
/* On 64-bit FreeBSD systems, clang -g -m32 is broken, but -m32 itself works OK. This has nothing to do with us, but let's avoid triggering that bug. */
if (!clang_mode || !m32_set) cc_params[cc_par_cnt++] = "-g";
#ifdef __APPLE__ if (!cc_params[0]) { SAYF("\n" cLRD "[-] " cRST "On Apple systems, 'gcc' is usually just a wrapper for clang. Please use the\n" " 'afl-clang' utility instead of 'afl-gcc'. If you really have GCC installed,\n" " set AFL_CC or AFL_CXX to specify the correct path to that compiler.\n"); FATAL("AFL_CC or AFL_CXX required on MacOS X"); } #endif
s32 pid; u32 rand_seed; int status; u8* inst_ratio_str = getenv("AFL_INST_RATIO");
structtimeval tv; structtimezone tz;
clang_mode = !!getenv(CLANG_ENV_VAR);
if (isatty(2) && !getenv("AFL_QUIET")) {
SAYF(cCYA "afl-as " cBRI VERSION cRST " by <lcamtuf@google.com>\n"); } else be_quiet = 1;
if (argc < 2) {
SAYF("\n" "This is a helper application for afl-fuzz. It is a wrapper around GNU 'as',\n" "executed by the toolchain whenever using afl-gcc or afl-clang. You probably\n" "don't want to run this program directly.\n\n"
"Rarely, when dealing with extremely complex projects, it may be advisable to\n" "set AFL_INST_RATIO to a value less than 100 in order to reduce the odds of\n" "instrumenting every discovered branch.\n\n");
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || inst_ratio > 100) FATAL("Bad value of AFL_INST_RATIO (must be between 0 and 100)");
}
if (getenv(AS_LOOP_ENV_VAR)) FATAL("Endless loop when calling 'as' (remove '.' from your PATH)");
setenv(AS_LOOP_ENV_VAR, "1", 1);
/* When compiling with ASAN, we don't have a particularly elegant way to skip ASAN-specific branches. But we can probabilistically compensate for that... */
/* Although this is not documented, GCC also uses TEMP and TMP when TMPDIR is not set. We need to check these non-standard variables to properly handle the pass_thru logic later on. */
if (!tmp_dir) tmp_dir = getenv("TEMP"); if (!tmp_dir) tmp_dir = getenv("TMP"); if (!tmp_dir) tmp_dir = "/tmp";
if (input_file[1]) FATAL("Incorrect use (not called through afl-gcc?)"); else input_file = NULL;
} else {
/* Check if this looks like a standard invocation as a part of an attempt to compile a program, rather than using gcc on an ad-hoc .s file in a format we may not understand. This works around an issue compiling NSS. */
if (outfd < 0) PFATAL("Unable to write to '%s'", modified_file);
outf = fdopen(outfd, "w");
if (!outf) PFATAL("fdopen() failed");
while (fgets(line, MAX_LINE, inf)) {
/* In some cases, we want to defer writing the instrumentation trampoline until after all the labels, macros, comments, etc. If we're in this mode, and if the line starts with a tab followed by a character, dump the trampoline now. */
/* Output the actual line, call it a day in pass-thru mode. */
fputs(line, outf);
if (pass_thru) continue;
/* All right, this is where the actual fun begins. For one, we only want to instrument the .text section. So, let's keep track of that in processed files - and let's set instr_ok accordingly. */
if (line[0] == '\t' && line[1] == '.') {
/* OpenBSD puts jump tables directly inline with the code, which is a bit annoying. They use a specific format of p2align directives around them, so we use that as a signal. */
/* Detect off-flavor assembly (rare, happens in gdb). When this is encountered, we set skip_csect until the opposite directive is seen, and we do not instrument. */
if (strstr(line, ".code")) {
if (strstr(line, ".code32")) skip_csect = use_64bit; if (strstr(line, ".code64")) skip_csect = !use_64bit;
}
/* Detect syntax changes, as could happen with hand-written assembly. Skip Intel blocks, resume instrumentation when back to AT&T. */
if (strstr(line, ".intel_syntax")) skip_intel = 1; if (strstr(line, ".att_syntax")) skip_intel = 0;
if (strstr(line, "#APP")) skip_app = 1; if (strstr(line, "#NO_APP")) skip_app = 0;
}
/* If we're in the right mood for instrumenting, check for function names or conditional labels. This is a bit messy, but in essence, we want to catch: ^main: - function entry point (always instrumented) ^.L0: - GCC branch label ^.LBB0_0: - clang branch label (but only in clang mode) ^\tjnz foo - conditional branches ...but not: ^# BB#0: - clang comments ^ # BB#0: - ditto ^.Ltmp0: - clang non-branch labels ^.LC0 - GCC non-branch labels ^.LBB0_0: - ditto (when in GCC mode) ^\tjmp foo - non-conditional jumps Additionally, clang and GCC on MacOS X follow a different convention with no leading dots on labels, hence the weird maze of #ifdefs later on. */
/* Conditional branch instruction (jnz, etc). We append the instrumentation right after the branch (to instrument the not-taken path) and at the branch destination label (handled later on). */
/* An optimization is possible here by adding the code only if the label is mentioned in the code in contexts other than call / jmp. That said, this complicates the code by requiring two-pass processing (messy with stdin), and results in a speed gain typically under 10%, because compilers are generally pretty good about not generating spurious intra-function jumps. We use deferred output chiefly to avoid disrupting .Lfunc_begin0-style exception handling calculations (a problem on MacOS X). */
if (!skip_next_label) instrument_next = 1; else skip_next_label = 0;
}
} else {
/* Function label (always instrumented, deferred mode). */
instrument_next = 1; }
}
}
if (ins_lines) fputs(use_64bit ? main_payload_64 : main_payload_32, outf);
/* Conditional branch instruction (jnz, etc). We append the instrumentation right after the branch (to instrument the not-taken path) and at the branch destination label (handled later on). */