change dlopen solution
MODIFY
docs/Changelog.md
docs/Changelog.md
@@ -9,9 +9,14 @@ Want to stay in the loop on major new features? Join our mailing list by
sending a mail to <afl-users+subscribe@googlegroups.com>.
### Version ++3.15a (dev)
- - documentation restructuring, made possible by Google Season of Docs :)
+ - documentation restructuring, made possible by Google Season of Docs
- new binary-only fuzzing mode: coresight_mode for aarch64 CPUs :)
thanks to RICSecLab submitting!
+ - if instrumented libaries are dlopen()'ed after the forkserver you
+ will now see crashes. before you would have colliding coverage.
+ we changed this to force fixing a broken setup rather then allowing
+ ineffective fuzzing.
+ See docs/best_practices.md how to fix such setups.
- afl-fuzz:
- cmplog binaries will need to be recompiled for this version
(it is better!)

MODIFY
docs/best_practices.md
docs/best_practices.md
### Targets
* [Fuzzing a target with source code available](#fuzzing-a-target-with-source-code-available)
+* [Fuzzing a target with dlopen() instrumented libraries](#fuzzing-a-target-with-dlopen-instrumented-libraries)
* [Fuzzing a binary-only target](#fuzzing-a-binary-only-target)
* [Fuzzing a GUI program](#fuzzing-a-gui-program)
* [Fuzzing a network service](#fuzzing-a-network-service)
To learn how to fuzz a target if source code is available, see [fuzzing_in_depth.md](fuzzing_in_depth.md).
+### Fuzzing a target with dlopen instrumented libraries
+
+If a source code based fuzzing target loads instrumented libraries with
+dlopen() after the forkserver has been activated and non-colliding coverage
+instrumentation is used (PCGUARD (which is the default), or LTO), then this
+an issue, because this would enlarge the coverage map, but afl-fuzz doesn't
+know about it.
+
+The solution is to use `AFL_PRELOAD` for all dlopen()'ed libraries to
+ensure that all coverage targets are present on startup in the target,
+even if accessed only later with dlopen().
+
+For PCGUARD instrumentation `abort()` is called if this is detected, for LTO
+there will either be no coverage for the instrumented dlopen()'ed libraries or
+you will see lots of crashes in the UI.
+
+Note that this is not an issue if you use the inferiour `afl-gcc-fast`,
+`afl-gcc` or`AFL_LLVM_INSTRUMENT=CLASSIC/NGRAM/CTX afl-clang-fast`
+instrumentation.
+
### Fuzzing a binary-only target
For a comprehensive guide, see

MODIFY
instrumentation/SanitizerCoverageLTO.so.cc
instrumentation/SanitizerCoverageLTO.so.cc
@@ -1289,6 +1289,18 @@ void ModuleSanitizerCoverage::instrumentFunction(
if (!Callee) continue;
if (callInst->getCallingConv() != llvm::CallingConv::C) continue;
StringRef FuncName = Callee->getName();
+ if (!FuncName.compare(StringRef("dlopen")) ||
+ !FuncName.compare(StringRef("_dlopen"))) {
+
+ fprintf(stderr,
+ "WARNING: dlopen() detected. To have coverage for a library "
+ "that your target dlopen()'s this must either happen before "
+ "__AFL_INIT() or you must use AFL_PRELOAD to preload all "
+ "dlopen()'ed libraries!\n");
+ continue;
+
+ }
+
if (FuncName.compare(StringRef("__afl_coverage_interesting"))) continue;
Value *val = ConstantInt::get(Int32Ty, ++afl_global_id);

MODIFY
instrumentation/SanitizerCoveragePCGUARD.so.cc
instrumentation/SanitizerCoveragePCGUARD.so.cc
@@ -851,6 +851,18 @@ bool ModuleSanitizerCoverage::InjectCoverage(Function & F,
if (!Callee) continue;
if (callInst->getCallingConv() != llvm::CallingConv::C) continue;
StringRef FuncName = Callee->getName();
+ if (!FuncName.compare(StringRef("dlopen")) ||
+ !FuncName.compare(StringRef("_dlopen"))) {
+
+ fprintf(stderr,
+ "WARNING: dlopen() detected. To have coverage for a library "
+ "that your target dlopen()'s this must either happen before "
+ "__AFL_INIT() or you must use AFL_PRELOAD to preload all "
+ "dlopen()'ed libraries!\n");
+ continue;
+
+ }
+
if (FuncName.compare(StringRef("__afl_coverage_interesting"))) continue;
cnt_cov++;

MODIFY
instrumentation/afl-compiler-rt.o.c
instrumentation/afl-compiler-rt.o.c
@@ -1418,16 +1418,14 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
if (start == stop || *start) return;
// If a dlopen of an instrumented library happens after the forkserver then
- // we have a problem.
- // Should we abort()? This way a user would/could find out.
- // Currently we just do not instrument that lib, which is invisible.
+ // we have a problem as we cannot increase the coverage map anymore.
if (__afl_already_initialized_forkserver) {
fprintf(stderr,
- "[-] ERROR: forkserver is already up, but an instrumented dlopen() "
- "library loaded afterwards. You must LD_PRELOAD such libraries to "
- "be able to fuzz them.\n");
- return; // or should be abort()?
+ "[-] FATAL: forkserver is already up, but an instrumented dlopen() "
+ "library loaded afterwards. You must AFL_PRELOAD such libraries to "
+ "be able to fuzz them or LD_PRELOAD to run outside of afl-fuzz.\n");
+ abort();
}
@@ -1443,6 +1441,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
/* instrumented code is loaded *after* our forkserver is up. this is a
problem. We cannot prevent collisions then :( */
+ /*
if (__afl_already_initialized_forkserver &&
__afl_final_loc + 1 + stop - start > __afl_map_size) {
@@ -1475,6 +1474,8 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
}
+ */
+
/* 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). */

MODIFY
test/test-dlopen.c
test/test-dlopen.c
int main(int argc, char **argv) {
- if (!getenv("TEST_DLOPEN_TARGET")) return 1;
+ if (!getenv("TEST_DLOPEN_TARGET")) {
+
+ fprintf(stderr, "Error: TEST_DLOPEN_TARGET not set!\n");
+ return 1;
+
+ }
+
void *lib = dlopen(getenv("TEST_DLOPEN_TARGET"), RTLD_LAZY);
if (!lib) {
@@ -15,8 +21,18 @@ int main(int argc, char **argv) {
}
int (*func)(int, char **) = dlsym(lib, "main_exported");
- if (!func) return 3;
+ if (!func) {
+
+ fprintf(stderr, "Error: main_exported not found!\n");
+ return 3;
+
+ }
+
+ // must use deferred forkserver as otherwise afl++ instrumentation aborts
+ // because all dlopen() of instrumented libs must be before the forkserver
+ __AFL_INIT();
+ fprintf(stderr, "Running main_exported\n");
return func(argc, argv);
}

change dlopen solution