This is an archive of the discontinued LLVM Phabricator instance.

More user-friendly libFuzzer flag
ClosedPublic

Authored by george.karpenkov on Apr 18 2017, 2:44 PM.

Details

Reviewers
kcc
Summary

Currently, adding libfuzzer support is quite involved:

  • User needs to manually link libFuzzer.a (which previously they needed to build themselves)
  • User needs to specify coverage flags (-fsanitize-coverage-trace-pc-guard and friends), which are very long, and from my understanding subject to change.
  • When compiling C files, user also would need to link libcxx.

With this patch we aim to simplify the procedure, requiring a single -fsanitize=fuzzer flag instead, which automatically inserts the required options.

Diff Detail

Event Timeline

kcc edited edge metadata.Apr 18 2017, 3:31 PM

It makes sense to have some shorter flag combination, I've been asked for it a couple of times.
One more reason for such flag is that I hope to have more/better/other coverage instrumentation and I don't want to make all users change their build rules.

It's not totally clear what such flag combination should be.

-fsanitize=fuzzer is a bit confusing, because it actually behaves differently compared to all other -fsanitize=foo (e.g. it provides main())

Another problem (not on OSX) is with msan. What if the user does -fsanitize=memory,fuzzer?
Right now this needs the libFuzzer itself to be msan-instrumented. :(

But ok, let's start from here

And yes, I'd really appreciate a linux implementation and documentation update.

it provides main()

We actually had a counterintuitive behavior while linking libfuzzer with something which has main already:

  • if the target file already has main, it is executed. Lack of LLVMFuzzerTestOneInput is ignored.
  • otherwise, fuzzing is done using LLVMFuzzerTestOneInput.
  • If neither LLVMFuzzerTestOneInputnor main are present, linker complains about the lack of LLVMFuzzerTestOneInput.

We were not able to figure out what causes such behavior, hints will be appreciated.

And yes, I will upload docs and linux support shortly.

kcc added a comment.Apr 18 2017, 3:40 PM

We were not able to figure out what causes such behavior, hints will be appreciated.

This is a linker-dependent behavior, which is hard to predict (a form of ODR violation).
Also depends on the order of the arguments to the linker.
E.g. on Linux:
% cat main.cc
int main() {}
% clang++ main.cc libFuzzer.a && ./a.out

produces a.out with empty main()

% clang++ libFuzzer.a main.cc
main.cc:(.text+0x0): multiple definition of `main'

george.karpenkov edited the summary of this revision. (Show Details)

I've added and tested linux support, documentation coming in a separate patch (a different repo).

kcc added a comment.Apr 19 2017, 5:08 PM

Please also add one full test with -fsanitize=fuzzer in lib/Fuzzer/test (probably, will need to create a subdir). Ok to have it in a separate change.

kcc accepted this revision.Apr 19 2017, 5:08 PM

LGTM

This revision is now accepted and ready to land.Apr 19 2017, 5:08 PM

Committed in 301212

Please also add one full test

I assume you don't want to simply change the flag in test/CMakeLists.txt to -fsanitize=fuzzer, and you would prefer to specify the coverage explicitly for tests?

As for the test, would you prefer to simply duplicate an existing test, or write a similar new one?

kcc added a comment.Aug 10 2017, 2:56 PM

There is a problem with -fsanitize=fuzzer

It's both a compiler flag and a linker flags (just as -fsanitize=address), which is convenient since the users don't need to specify two flags.
But it's also a trouble because now if you set CFLAGS="-fsanitize=fuzzer" in some build systems (I've hit it in libpng) this flag will also be used for linking during the configure step and will cause failures,
because -fsanitize=fuzzer is supposed to work only with fuzz targets (a library w/o main and with LLVFuzzerTestOneInput).

Thoughts?

@kcc hmm that's strange, I did check logic *not* to link libLLVMFuzzer if output is a shared object. What is the output in your case? Is Clang recent?

@kcc otherwise even libFuzzer tests would fail, as they produce shared objects.

kcc added a comment.Aug 10 2017, 3:02 PM

I am using TotT clang to build libpng with fsanitize=fuzzer and libpng's configure script fails
configure: error: C compiler cannot create executables

And of course it does!
It creates a tiny .c file with main() and tries to build it:
configure:3306: clang -O2 -fno-omit-frame-pointer -g -fsanitize=address,fuzzer conftest.c >&5
/tmp/conftest-e56ae6.o: In function `main':
...BUILD/conftest.c:14: multiple definition of `main'

If adding -fsanitize=fuzzer into CFLAGS enables it for linking as well and if that's undesired, how about adding -fno-sanitize=fuzzer into LDFLAGS?

kcc added a comment.Aug 10 2017, 3:11 PM

OMG no.

I was thinking maybe to have a separate linker flag (-lfuzzer? -llibFuzzer? -libFuzzer?).
Yes, annoying to pass another flag, but I don't see a better solution for this. At least not yet

@kcc I guess I didn't have this problem yet as somehow on macos the symbol collision seems to be resolved "properly" in most cases.
Honestly the way I would do it is by providing a function run_fuzzer which would take a function pointer with an explicit callback.

But that's a large change, and I assume you would not want that?

For the libpng case, you can not link off-the-shelf library with libFuzzer anyway, right? As it does not have LLVMFuzzerTestOneInput symbol.
So the solution seems to be not to compile it with -fsanitize=fuzzer, but only with coverage flags, and then compile and link the fuzzing driver itself
with -fsanitize=fuzzer.
If there are too many coverage flags (which also keep changing) maybe we could also have -fsanitize=coverage or similar? In the driver then,
-fsanitize=fuzzer would link libc++ and imply -fsanitize=coverage.

kcc added a comment.Aug 10 2017, 4:00 PM

@kcc I guess I didn't have this problem yet as somehow on macos the symbol collision seems to be resolved "properly" in most cases.
Honestly the way I would do it is by providing a function run_fuzzer which would take a function pointer with an explicit callback.

And then all fuzzing engines will have a different API, and we won't have engine interoperability.

But that's a large change, and I assume you would not want that?

For the libpng case, you can not link off-the-shelf library with libFuzzer anyway, right?

It's not the libarary, it's the configure tests that the build system uses (most GNU libraries use it)

As it does not have LLVMFuzzerTestOneInput symbol.
So the solution seems to be not to compile it with -fsanitize=fuzzer, but only with coverage flags, and then compile and link the fuzzing driver itself
with -fsanitize=fuzzer.

Yes.

If there are too many coverage flags (which also keep changing) maybe we could also have -fsanitize=coverage or similar? In the driver then,
-fsanitize=fuzzer would link libc++ and imply -fsanitize=coverage.

Yes, that will probably be ok (except the name. maybe -fsanitize=fuzzer_coverage?)

@kcc I'm fine with either name.

kcc added a comment.Aug 10 2017, 4:12 PM

Naming is hard. maybe -fsanitize=fuzzer-no-link?

@kcc yeah better than having underscores.

kcc added a comment.Aug 10 2017, 4:39 PM

Than -fsanitize=fuzzer-no-link :)
Will you have time to make this change?

@kcc OK I think we need it anyway, since relying on correct "main" resolution is fragile.