-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a libFuzzer fuzzing harness #744
base: master
Are you sure you want to change the base?
Conversation
Maybe I should add more "rainy day" scenarios? (ie if I can decode a pubkey+signature from the input, try to verify it and check that it returns 0) |
This is an excellent initiative! Thanks a lot for doing this! ❤️ Strongest possible concept ACK from a fellow fuzzing enthusiast :) Yesterday I setup a very basic fuzzing infrastructure which does continuous fuzzing of I'd be happy to share all coverage increasing inputs I'm able to find. (In the extremely unlikely event that I find a crashing input (crashing with or without sanitizers enabled) it will be responsibly reported to the maintainers like I've done in the past when I've found issues in Bitcoin Core or any of its dependencies (CVE-2017-18350, CVE-2018-20586, CVE-2019-18936, etc.). I believe in responsible disclosure and the policy I'm following myself is that the project maintainer decides when to go public with any findings and ideally that the maintainer handles all such public disclosure to avoid having the researcher accidentally disclosing more than the maintainer signed off for due to miscommunication.) A few comments/suggestions regarding the implementation that I thought of when I tinkered with your code while setting up the continuous fuzzing:
Again: @elichai - this is a great initative. Thanks! |
Thanks :)
I've done it like that because I only added support for libFuzzer right now, didn't want to add the complexity Bitcoin Core has for AFL because I wasn't sure if anyone was using it and it can be done in a future PR, but if it's a common setup I can definitely adapt.
I also thought about it, right now we have different benchmarks binary per module, but one test binary for all modules. P.S. what do you think about the parsing loops, are they a good idea or that's not really how the fuzzing is designed to be used? (I'm iterating byte by byte over the whole input trying it as input) |
Have you checked what parts of the code your fuzzer is able to reach deeply in to, and what parts that are only shallowly fuzzed when starting from an empty seed corpus? Due to the nature of the library there are quite a few natural "fuzzing road blocks" in the code base that will need to be handled to get good/deep coverage when doing coverage-based fuzzing starting from an empty corpus. John Regehr has a great piece on writing fuzzable code which also covers "fuzzer blockers". I highly recommend Regehr's blog if you're interested in modern fuzzing and modern fuzzing research. Here are some good reads:
Happy fuzzing! :) |
I'll read, thank you.
How do I check that? And it should be able to reach pretty deeply in the "happy path"(because a valid seckey is pretty probable, and then I sign, verify etc with it) |
This is the type of output you would like to be looking at when searching for opportunities to tweak your fuzzer to reach deeper: https://marcofalke.github.io/btc_cov/fuzz.coverage/index.html It shows the current fuzzing coverage of Bitcoin Core. It is currently at 50% line coverage but I'm far from done. I'm aiming for 1.) beating the unit tests with regards to coverage (short term), 2.) beating the functional tests with regards to coverage (medium term) and 3.) in the longer term the final goal is full coverage of course :) See if you can create such a page with I would suggest first fuzzing normally using say:
Let it run until you get bored waiting for entries marked Stop the fuzzer. Now go build a coverage instrumented build of Call the resulting binary Do ... for INPUT in thin-air-corpus/*; do
./fuzz_public_cov_without_libfuzzer < $INPUT
done ... to collect coverage data. Use Tweak your fuzzing harness to achieve even better coverage. Repeat the whole process. Again. And again. And again until you've reached a level of coverage you're pleased with :)
All paths are good to fuzz, but generally speaking if I'd have to choose between fuzzing only "happy paths" or only non-"happy paths" to find vulnerabilities I'd go for non-happy paths (paths likely to invoke error handling, etc.). Those are usually relatively under-tested and thus the places where you are most likely to find interesting stuff :) |
I've now created a repo (https://github.com/practicalswift/libsecp256k1-fuzzing-seed-corpus) where we can share coverage increasing inputs :) I've added a first batch of the 2911 coverage increasing inputs I've found using a fuzzing farm that has been running non-stop for 12 days (calendar time, CPU time much much more of course). Hope this first set of inputs can help you find opportunities where the fuzzing harness can be tweaked to reach deeper in to the code base and thus achieve better coverage :) Really excited about your fuzzing work! Quick start guide if anyone else wants to follow along on this fuzzing journey:
|
Thanks! This is awesome. |
@elichai Excellent! Let me know if I can help in any way :) |
There's one thing I'm trying to figure out Right now I'm mostly reusing the same data for a lot of operations, and the whole "Fuzz Garbage through parsing functions" basically throws all the input into those functions. But if I shouldn't use the same input for multiple functions(which if I look at (P.S. Are you on IRC or any chat platform?) |
Re-using the same parts of the input for independent parts of the fuzzing harness is likely to confuse the coverage-guided fuzzer and should be avoided if possible. Example: If you're fuzzing the
… is much better than …
… since the fuzzer in the first case can try to figure out the relation between Generally you want to make everything as dumb as possible for the coverage-guided fuzzer to work: no input re-use which may obscure the true interaction between input provided and coverage reached, and many small target binaries (testing a single unit of of functionality each) instead of one big target binary trying to cover all functionality. In the example above then Hope that helps :)
I don't use IRC, but I'll send you an e-mail right away :) |
I believe this is getting better. |
@elichai Yes, please split in multiple binaries: see the last point in these recommendations from the
|
Anyone wants to chime in with Concept ACK:s or Concept NACK:s? (FWIW I'm super enthusiastic about your work @elichai, and if it doesn't end up in this repo I'd like to collaborate with you on this effort in another repo (say |
I rally don't know a lot about fuzzing so I doubt I'm qualified enough to judge this. What's the cost of maintaining this? Does it have advantages to keep this in this repo here (because it may be extended to the internal API) or can this live equally good in another repo? |
I think a rebase is needed :) Somewhat related to this PR: @guidovranken is doing some really interesting differential fuzzing work which AFAIK haven't found any bugs in libsecp256k1, but in some other libraries like Trezor: trezor/trezor-firmware#1374. Perhaps it could serve as inspiration for your fuzzing work @elichai, or you could join forces? :) |
Small note, I'm not sure it's that obvious that a zero message should pass ECDSA verification (see rust-bitcoin/rust-secp256k1#207) but yeah I don't mind helping fuzzing / rebasing, and I should probably add schnorrsig coverage here at some point |
Sorry for the late response, but Concept ACK. While the general advice is indeed to have multiple fuzzing binaries for different tests (and specifically, not read the "which fuzz test to run" from the test input, as the fuzzing engine will try to mutate pointlessly between the tests), in Bitcoin Core we discovered this quickly becomes unwieldy when there are lots of tests. We instead settled for reading the test name from the environment (so, one binary, but it needs to be told through Going further than this, I think the most interesting types of test will be more low-level tests (field, scalar arithmetic, perhaps group operations), and higher-level tests for the exhaustive test field. Having this is a great start, though. Rebase? |
Hi,
As a result of #739, I wrote a fuzzing harness so people can fuzz the library themselves if they want so.
This is my first time playing with libFuzzer so any feedback/criticism is very welcome.
For the autotools part it's mostly copied from bitcoin core with the caveat of automatically adding
-fsanitize=fuzzer,
because this is only libFuzzer so and that's required to run it anyway.Currently it's fuzzing only the public API of the library, but in the future we can add fuzzing for internal functions, which is why I created a
fuzz
directory.One thing I did that I know isn't quite standard is the
Fuzz Garbage through parsing functions
part, where I loop over the input byte by byte and feed it into the parsing functions, I think this is a good way to throw a lot of data into these functions and make sure that: A. they don't crash, B. they return only 1 or 0.cc @practicalswift