mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-24 06:42:42 +00:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
b6cfe5ceb0 | |||
15fbbce5c3 | |||
691795a16a | |||
9d22fad50c | |||
a0e400d08b | |||
8805483d0c |
@ -156,6 +156,7 @@ Then there are a few specific features that are only available in instrumentatio
|
||||
This defaults to 1
|
||||
- `AFL_LLVM_LTO_DONTWRITEID` prevents that the highest location ID written
|
||||
into the instrumentation is set in a global variable
|
||||
- `AFL_LLVM_LTO_CALLGRAPH` will not generate a binary but a callgraph instead
|
||||
|
||||
See [instrumentation/README.lto.md](../instrumentation/README.lto.md) for more information.
|
||||
|
||||
|
@ -102,8 +102,9 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_LLVM_INSTRUMENT_FILE",
|
||||
"AFL_LLVM_SKIP_NEVERZERO",
|
||||
"AFL_NO_AFFINITY",
|
||||
"AFL_LLVM_LTO_STARTID",
|
||||
"AFL_LLVM_LTO_CALLGRAPH",
|
||||
"AFL_LLVM_LTO_DONTWRITEID",
|
||||
"AFL_LLVM_LTO_STARTID",
|
||||
"AFL_NO_ARITH",
|
||||
"AFL_NO_AUTODICT",
|
||||
"AFL_NO_BUILTIN",
|
||||
|
106
src/afl-cc.c
106
src/afl-cc.c
@ -31,6 +31,8 @@
|
||||
#include <strings.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#if (LLVM_MAJOR - 0 == 0)
|
||||
#undef LLVM_MAJOR
|
||||
@ -57,6 +59,7 @@ static u8 * lto_flag = AFL_CLANG_FLTO, *argvnull;
|
||||
static u8 debug;
|
||||
static u8 cwd[4096];
|
||||
static u8 cmplog_mode;
|
||||
static u8 * ll_file;
|
||||
u8 use_stdin; /* dummy */
|
||||
// static u8 *march_opt = CFLAGS_OPT;
|
||||
|
||||
@ -315,6 +318,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, shared_linking = 0,
|
||||
preprocessor_only = 0, have_unroll = 0, have_o = 0, have_pic = 0,
|
||||
have_c = 0;
|
||||
u32 out_idx = 0;
|
||||
|
||||
cc_params = ck_alloc((argc + 128) * sizeof(u8 *));
|
||||
|
||||
@ -741,6 +745,8 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
cc_params[cc_par_cnt++] = cur;
|
||||
|
||||
if (!strcmp(cur, "-o")) { out_idx = cc_par_cnt; }
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_HARDEN")) {
|
||||
@ -801,7 +807,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (!getenv("AFL_DONT_OPTIMIZE")) {
|
||||
if (!getenv("AFL_DONT_OPTIMIZE") && !getenv("AFL_LLVM_LTO_CALLGRAPH")) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-g";
|
||||
if (!have_o) cc_params[cc_par_cnt++] = "-O3";
|
||||
@ -947,7 +953,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
#ifndef __ANDROID__
|
||||
|
||||
if (compiler_mode != GCC && compiler_mode != CLANG) {
|
||||
if (compiler_mode != GCC && compiler_mode != CLANG && !getenv("AFL_LLVM_LTO_CALLGRAPH")) {
|
||||
|
||||
switch (bit_mode) {
|
||||
|
||||
@ -1010,6 +1016,23 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
#endif
|
||||
|
||||
if (getenv("AFL_LLVM_LTO_CALLGRAPH")) {
|
||||
|
||||
if (!lto_mode) { FATAL("AFL_LLVM_LTO_CALLGRAPH requires LTO mode"); }
|
||||
cc_params[cc_par_cnt++] = "-O0";
|
||||
cc_params[cc_par_cnt++] = "-w";
|
||||
|
||||
if (!have_c) {
|
||||
|
||||
ll_file = alloc_printf("%s.ll", cc_params[out_idx]);
|
||||
cc_params[out_idx] = ll_file;
|
||||
cc_params[cc_par_cnt++] = "-S";
|
||||
cc_params[cc_par_cnt++] = "-emit-llvm";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cc_params[cc_par_cnt] = NULL;
|
||||
|
||||
}
|
||||
@ -1655,8 +1678,9 @@ int main(int argc, char **argv, char **envp) {
|
||||
" AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a "
|
||||
"global var\n"
|
||||
" AFL_LLVM_LTO_STARTID: from which ID to start counting from for "
|
||||
"a "
|
||||
"bb\n"
|
||||
"a bb\n"
|
||||
" AFL_LLVM_LTO_CALLGRAPH: will not generate a binary but a "
|
||||
"callgraph instead\n"
|
||||
" AFL_REAL_LD: use this lld linker instead of the compiled in "
|
||||
"path\n"
|
||||
"If anything fails - be sure to read README.lto.md!\n");
|
||||
@ -1880,9 +1904,79 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
execvp(cc_params[0], (char **)cc_params);
|
||||
s32 pid = 0;
|
||||
if (ll_file) { pid = fork(); } // CG run
|
||||
|
||||
FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
|
||||
if (!pid) {
|
||||
|
||||
execvp(cc_params[0], (char **)cc_params);
|
||||
FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
|
||||
|
||||
}
|
||||
|
||||
// only for AFL_LLVM_LTO_CALLGRAPH
|
||||
s32 status;
|
||||
if (waitpid(pid, &status, 0) <= 0) { PFATAL("waitpid() failed"); }
|
||||
|
||||
u8 *cg_file = (u8 *)ck_strdup(ll_file);
|
||||
cg_file[strlen(cg_file) - 2] = 0;
|
||||
strcat(cg_file, "cg");
|
||||
if (!be_quiet) SAYF("Generating callgraph into %s ...\n", cg_file);
|
||||
u8 *opt_cmd = (u8 *)ck_alloc(strlen(cc_params[0]) + 16);
|
||||
strcpy(opt_cmd, cc_params[0]);
|
||||
if ((ptr = (u8 *)strrchr(opt_cmd, '/'))) {
|
||||
|
||||
++ptr;
|
||||
|
||||
} else {
|
||||
|
||||
ptr = opt_cmd;
|
||||
|
||||
}
|
||||
|
||||
u8 *dash = (u8 *)strchr(opt_cmd, '-');
|
||||
strcpy((char *)opt_cmd, "opt");
|
||||
if (dash) {
|
||||
|
||||
dash = (u8 *)strrchr(cc_params[0], '/');
|
||||
dash = (u8 *)strchr(dash, '-');
|
||||
strcat((char *)opt_cmd, (char *)dash);
|
||||
|
||||
}
|
||||
|
||||
u8 *opt_params[8];
|
||||
u32 opt_par_cnt = 0;
|
||||
opt_params[opt_par_cnt++] = opt_cmd;
|
||||
opt_params[opt_par_cnt++] = "-O0";
|
||||
opt_params[opt_par_cnt++] = "--analyze";
|
||||
opt_params[opt_par_cnt++] = "--dot-callgraph";
|
||||
opt_params[opt_par_cnt++] = "-o";
|
||||
opt_params[opt_par_cnt++] = cg_file;
|
||||
opt_params[opt_par_cnt++] = ll_file;
|
||||
opt_params[opt_par_cnt] = NULL;
|
||||
|
||||
if (debug) {
|
||||
|
||||
DEBUGF("cd '%s';", getthecwd());
|
||||
for (i = 0; i < (s32)opt_par_cnt; i++)
|
||||
SAYF(" '%s'", opt_params[i]);
|
||||
SAYF("\n");
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (!pid) {
|
||||
|
||||
execvp(opt_params[0], (char **)opt_params);
|
||||
FATAL("Oops, failed to execute '%s' - check your PATH", opt_params[0]);
|
||||
|
||||
}
|
||||
|
||||
if (waitpid(pid, &status, 0) <= 0) { PFATAL("waitpid() failed"); }
|
||||
if (!be_quiet) SAYF("Done, delete '%s' now.\n", ll_file);
|
||||
|
||||
return 0;
|
||||
|
||||
|
91
utils/callgraph_analysis/callgraph_analysis.py
Executable file
91
utils/callgraph_analysis/callgraph_analysis.py
Executable file
@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# american fuzzy lop++ - callgraph analysis script
|
||||
# ------------------------------------------------
|
||||
#
|
||||
# (c) 2021 Marc Heuse <mh@mh-sec.de>
|
||||
#
|
||||
# python script a single parameter: fuzz.cg
|
||||
# fuzz.cg is generated with with afl-clang-lto + AFL_LLVM_LTO_CALLGRAPH=1
|
||||
#
|
||||
# 1. run c++filt on both .cg inputs
|
||||
# 2. parse fuzz-harness.cg, remove all functions that just have on external call
|
||||
# keep an array of it's functions and the one it is calling
|
||||
# 3. parse fuzz.cg, remove all functions that just have an external call.
|
||||
# 4. loop on fuzz-harness.cg array, look for these functions in fuzz.cg,
|
||||
# remove them from both fuzz-harness.cg and fuzz.cg array when found and
|
||||
# add the callers into fuzz-harness.cg array instead.
|
||||
# 5. resulting functions in fuzz-harness.cg array are unreachable
|
||||
#
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
|
||||
new_function = re.compile('^Call graph node for function:')
|
||||
is_external = re.compile('calls external')
|
||||
is_call = re.compile('calls function')
|
||||
is_empty = re.compile('^\s*$')
|
||||
|
||||
all_functions = []
|
||||
entries = []
|
||||
calls = []
|
||||
entry = { 'caller': '', 'calls': [''] }
|
||||
func = ''
|
||||
i = 0
|
||||
|
||||
print()
|
||||
print('Analysing ' + sys.argv[1] + " for unreachable functions...")
|
||||
|
||||
with open(sys.argv[1]) as f:
|
||||
for line in f:
|
||||
#print(line)
|
||||
if new_function.match(line):
|
||||
if not len(func) == 0:
|
||||
print('Error: invalid input: ' + line)
|
||||
exit(1)
|
||||
func = line.split("'")[1]
|
||||
if len(func) > 0:
|
||||
all_functions.append(func)
|
||||
calls.clear()
|
||||
elif is_call.search(line):
|
||||
if len(func) > 0:
|
||||
call = line.split("'")[1]
|
||||
calls.append(call);
|
||||
elif is_empty.match(line):
|
||||
if len(calls) > 0 and len(func) > 0:
|
||||
calls_u = list(dict.fromkeys(calls))
|
||||
entry = { 'func' : func, 'calls' : calls_u }
|
||||
entries.append(entry)
|
||||
func = ''
|
||||
calls.clear()
|
||||
elif not is_external.search(line):
|
||||
#print('Ignoring line: ' + line)
|
||||
i += 1 # ignored
|
||||
|
||||
found = 0
|
||||
for entry in entries:
|
||||
if entry['func'] == 'main':
|
||||
found = 1
|
||||
break
|
||||
if found:
|
||||
follow = ['main']
|
||||
else:
|
||||
follow = ['LLVMFuzzerInitialize', 'LLVMFuzzerTestOneInput']
|
||||
|
||||
for f in follow:
|
||||
for entry in entries:
|
||||
if entry['func'] == f:
|
||||
#print('follow: ' + f + ' => ' + str(entry['calls']))
|
||||
for call in entry['calls']:
|
||||
follow.append(call)
|
||||
entries.remove(entry)
|
||||
break
|
||||
|
||||
print('Analysis done, printing results.')
|
||||
print('Note that CTORs/DTORs/callbacks-only functions will be in the unreachable list.')
|
||||
print()
|
||||
|
||||
for entry in entries:
|
||||
print('Unreachable: ' + entry['func'])
|
Reference in New Issue
Block a user