Search code examples
fuzzinglibfuzzer

Is it possible to tell libfuzzer ignore certain code?


I use libfuzzer and it's been great experience so far. My code under fuzz is full of branches like this:

bool fuzzingThisFunc() {
  if(!checkSomething()) {
    fmt::printf("error log");
    return false;
  }

  ...

  return true;
}

Where fmt::printf is a function from a third party library (http://github.com/fmtlib/fmt).

I feel like after few iterations fuzzer enters this function and effectively starts fuzzing all branches inside it (like when it's using DFS instead of BFS).

I would like to add some barrier or instruction to a fuzzer to not insert instrumentation into third party libraries, so my fuzzer will try to cover only my code.

Is it possible?


Solution

  • Libfuzzer supports instrumentation on source file level. An option would be to build third party libraries without -fsanitize=fuzzer flag. Check CFLAGS passed to configure of these libraries, to remove this flag.

    Header-only libraries typically include templates, which is the case for fmt. They must be instantiated at compile time. I see no simple way to handle these. You can find all used template arguments, create thunk code that uses them with these arguments, exclude this code from instrumentation and modify your calling code to use these instantiated funcs, but this is very difficult.

    When the code you want to be not instrumented does only logging or other activities that can be skipped without modifying the behaviour of your application, you can make it conditional for compiling. Libfuzzer docs suggests to use FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION define to mark code which you don't want to build for fuzzing. In fmt case this would be:

    bool fuzzingThisFunc() {
      if(!checkSomething()) {
    #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
        fmt::printf("error log");
    #endif
        return false;
      }
    
      ...
    
      return true;
    }
    

    or modifying library code:

    template <typename S, typename... Args,
              FMT_ENABLE_IF(detail::is_string<S>::value)>
    inline int printf(const S& format_str, const Args&... args) {
    
    #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
      using context = basic_printf_context_t<char_t<S>>;
      return vprintf(to_string_view(format_str),
                     make_format_args<context>(args...));
    #else
      return 0; //C printf returns number of characters written, I assume same for fmt.
    #endif
    }
    

    The second case is easier to code (one modification per excluded func), but you have to add this modification everytime you get a new fmt version. In the first case you have to modify every excluded func call site.

    For both cases you should add -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION to CFLAGS of configure for fuzzing target build.