about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--docs/Changelog.md7
-rw-r--r--docs/best_practices.md21
-rw-r--r--instrumentation/SanitizerCoverageLTO.so.cc12
-rw-r--r--instrumentation/SanitizerCoveragePCGUARD.so.cc12
-rw-r--r--instrumentation/afl-compiler-rt.o.c15
-rw-r--r--test/test-dlopen.c20
6 files changed, 77 insertions, 10 deletions
diff --git a/docs/Changelog.md b/docs/Changelog.md
index 103f9f63..be5cac43 100644
--- a/docs/Changelog.md
+++ b/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!)
diff --git a/docs/best_practices.md b/docs/best_practices.md
index 979849f4..18096851 100644
--- a/docs/best_practices.md
+++ b/docs/best_practices.md
@@ -5,6 +5,7 @@
 ### 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)
@@ -20,6 +21,26 @@
 
 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
diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc
index bff85a0a..8d7f0c80 100644
--- a/instrumentation/SanitizerCoverageLTO.so.cc
+++ b/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);
diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc
index 3574b0e4..92450781 100644
--- a/instrumentation/SanitizerCoveragePCGUARD.so.cc
+++ b/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++;
diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c
index 65dafb8c..20f325f3 100644
--- a/instrumentation/afl-compiler-rt.o.c
+++ b/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). */
diff --git a/test/test-dlopen.c b/test/test-dlopen.c
index d08d9092..b81bab13 100644
--- a/test/test-dlopen.c
+++ b/test/test-dlopen.c
@@ -5,7 +5,13 @@
 
 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);
 
 }