Fuzzing a Go application with libfuzzer: wrong coverage

644 Views Asked by At

I'm writing my thesis and would greatly appreciate some input on my current setup. I want to fuzz a target Go application with libfuzzer and therefore did the following:

I wrote a main package that calls the go function to be tested. Code:

package main
import "C"
import "$PACKAGE"
func main(){}
//export Fuzz
func Fuzz(data []byte){
 $PACKAGE.Fuzz(data) //actual Fuzzing function, which instruments the function under test  
}

A main package is necessary for llvm-go to actually build the application as a shared library. The reason I have a double abstraction here is that I want my build pipeline to be compatible with go-fuzz, which uses the "Fuzz" function name as a fuzzing entry point. With my setup I can simply wrap around the code necessary to make use of libfuzzer.

Then I compile the application with llvm-go.

Now I have two ways to instrument the Go shared library: either load it dynamically or have it statically linked in my binary. I'll explain my issue using the statically linked code. For that I wrote some fuzzing stub that instruments the Go shared library.

// fuzz_target.cc
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <dlfcn.h>
#include "golang-app.h"

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
 void *handle;
 char *error; 
 GoSlice data = {(void*)Data, static_cast<long long>(Size), static_cast<long long>(Size)};
 Fuzz(data);
 return 0; // Non-zero return values are reserved for future use.
}

Then compiled it with:

clang -g -O1 -fsanitize=fuzzer,address -o fuzz fuzzing-entry_static.cc ../golang-app.so

Now, when I run my code I see two issues:

master@thesis:~/go-libfuzz$ ./fuzz-static ./corpus/ -timeout=12
INFO: Seed: 2260122321
INFO: Loaded 1 modules   (1 inline 8-bit counters): 1 [0x787f60, 0x787f61),
INFO: Loaded 1 PC tables (1 PCs): 1 [0x565bd0,0x565be0),
INFO:       11 files found in ./corpus/
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 18061 bytes
INFO: seed corpus: files: 11 min: 450b max: 18061b total: 76971b rss: 51Mb
#12     INITED cov: 1 ft: 1 corp: 1/450b exec/s: 0 rss: 53Mb
#13     REDUCE cov: 1 ft: 1 corp: 1/282b exec/s: 0 rss: 53Mb L: 282/282 MS: 1 EraseBytes-
[...]
#193    REDUCE cov: 1 ft: 1 corp: 1/1b exec/s: 0 rss: 56Mb L: 1/1 MS: 1 EraseBytes-
#65536  pulse  cov: 1 ft: 1 corp: 1/1b exec/s: 21845 rss: 63Mb
#131072 pulse  cov: 1 ft: 1 corp: 1/1b exec/s: 26214 rss: 68Mb
#262144 pulse  cov: 1 ft: 1 corp: 1/1b exec/s: 26214 rss: 79Mb
#524288 pulse  cov: 1 ft: 1 corp: 1/1b exec/s: 27594 rss: 100Mb
#1048576        pulse  cov: 1 ft: 1 corp: 1/1b exec/s: 27594 rss: 142Mb
#2097152        pulse  cov: 1 ft: 1 corp: 1/1b exec/s: 27962 rss: 226Mb

The coverage and features do not seem to increase and the memory requirement increases every run. I was able to fix the memory increase thanks to another SO post by adding an environment variable to the fuzzer execution. ASAN_OPTIONS=quarantine_size_mb=20 ./fuzz-target -timeout=12

However, probably the main issue is that the actual target is "buried" under two levels of abstraction: LLVMFuzzerTestOneInput --calls--> shared Go library via main.go, which contains exported Fuzz function (exposed via CGo) which --calls--> Fuzzing function --calls--> target function. Please note, however, that each of those functions just hands over the data directly to the next layer without making changes. What steps can I do to ensure this is working correctly? Am I doing something wrong?

0

There are 0 best solutions below