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?
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.