2

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?

warchantua
  • 1,154
  • 1
  • 10
  • 24
  • I'd be concerned that bugs in your code naturally manifest in narrow-contract 3rd-party APIs. In that case, including {fmt} in your search would be essential - if expensive. – John McFarlane Apr 22 '22 at 11:24
  • I'm curious to know what optimisation level are you using and how much does this help? – John McFarlane Apr 22 '22 at 11:24

1 Answers1

1

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.

nevilad
  • 932
  • 1
  • 7
  • 14
  • 1
    Interesting idea! How about header-only libraries? – warchantua Jan 22 '21 at 18:32
  • Changed my answer. Libfuzzer can explore fmt::printf if it can modify something, that affects it's internal behaviour. An obvious choice are function arguments. When they are hardcoded, like in case of fmt::printf("error log") it can't modify it. In this case your assumptions that libfuzzer gets stuck exploring fmt may be wrong. – nevilad Jan 23 '21 at 10:44
  • Once I disabled logging, my fuzzer immediately found an issue... I used compile switch to disable all loggers (luckily they were hidden under a macro, so I changed 1 place). – warchantua Jan 25 '21 at 18:12