-
Notifications
You must be signed in to change notification settings - Fork 38.7k
wip: Split fuzz binary #29010
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
wip: Split fuzz binary #29010
Conversation
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code CoverageFor detailed information about the code coverage, see the test coverage report. ReviewsSee the guideline for information on the review process. |
|
cc @maflcko turns out we can actually keep the map if we just don't add all the harness functions into it |
|
Nice, but I am not sure if we want to go down to route to put everyone back into the makefile hell, because this will just make writing new fuzz tests bloaty and impossible to maintain/review (Who is going to read those repetitive and ugly 1k LOC of build code). (Unrelated: Not even sure how this will interact with cmake) It seems easier to just put a one-line bash command into the readme to achieve the same, for anyone that needs it (oss-fuzz, fuzz-introspector, afl-lto, etc ...). Happy to provide more feedback, but for now it would be good to explain why each target needs to be in a separate file. In C++ it should be possible to have more than one function in the same file. Also, given that |
|
I split the harnesses into individual files so that we can use the file list in |
Are you sure on this? I don't know how afl or fuzz-introspector detect reachable code paths, but I'd be surprised if the static analysis can follow a function pointer hidden in a map. |
|
🐙 This pull request conflicts with the target branch and needs rebase. |
Yea maybe not... should be quite easy to avoid the map. I'll try to setup a fuzz-introspector instance at some point, I have a feeling it'll still be a while before we can use oss-fuzz's instance. |
What does this mean? I'm seeing file sizes of 448MB (with fuzzing enabled) and 146MB (with a normal build) for test/fuzz/fuzz. This seems like it's reverting #20560? |
yeah, in the current form this is reverting that pull, so I don't think it can be merged. Though, I guess the goal is to somehow come up with magic makefile code (which can optionally be enabled) to extend the build code from the file names automatically. |
|
I guess the "one fuzz target per file" makes the "one fuzz binary per message/rpc type" idea a bit harder to implement: #28015 (comment) ? |
If you build this pull request you should see a binary per fuzz harness in How I compiled this PR to get these stats: |
| name##_Before_Main() \ | ||
| { \ | ||
| if constexpr (should_compile_harness(#name)) { \ | ||
| FuzzFrameworkRegisterTarget(#name, name##_fuzz_target, {__VA_ARGS__}); \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the change in this header file needed for your approach? If there is only one fuzz target per file, and only one file is compiled, you wouldn't need to check whether it needs to be compiled, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I think once building all the individual binaries is optional, this could be used to build just one of the individual binaries, e.g.:
CPPFLAGS="-DFUZZ_HARNESS=process_message" ./configure --enable-fuzz
make
which would produce src/test/fuzz/fuzz that only has the process_message harness. Building and linking just one harness is faster than all of them, maybe useful when reproducing testcases?
Maybe there is a better way of accomplishing this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Building and linking just one harness is faster than all of them, maybe useful when reproducing testcases?
Not sure. This isn't supported for any other tests (bench, unit, ...), so maybe leave as a follow-up.
If building takes a long time, you can use more CPUs or a populated ccache.
Also, it could be done easier inside the makefile by just skipping over the other files, if needed?
Hmm, if I switch to this PR and try compiling, I get bunches of linker errors about multiple definitions, eg ./src/./tinyformat.h:666: multiple definition of `MakeV1Transport(long)'; test/fuzz/fuzz-addrman.o:./src/./tinyformat.h:666: first defined here Dropping from 14GB to 3.6GB seems worthwhile for oss-fuzz (and similar), but not worthwhile otherwise to me -- it's not compiling so I can't say for sure, but 450MB bloating out to 3.6GB sounds terrible? Also LTO seems pretty slow, and not amenable to ccache... Shouldn't it be possible to just setup test/fuzz/fuzz.cpp so it (alone) can be recompiled with You could maybe use something like: $ find -name '*.cpp' | sort | while read a; do grep ^FUZZ_TARGET $a | sed 's/^FUZZ_TARGET[_A-Z]*[(] *\([^,) ]*\) *[,)].*/\1/'; doneto get a list of fuzz targets without having to compile the code first, or separate things into different files. |
Yea weird, it compiles for me with
I listed more benefits in #28971. It is not gonna have a noticeable effect for contributors that aren't regularly fuzzing.
The splitting is super straight forward though and the more maintainable thing IMO (your script does not work for e.g.
After consulting our build system gods, it looks like this is not possible with automake. It is apparently very easy to do with cmake though, so I guess we can consider this blocked until cmake is in. |
|
I'll re-open this once we switched to cmake. I've rebased the commits on the current cmake staging branch and building individual binaries using cmake is quite easy (assuming there is one harness per file): file(GLOB fuzz_harness_file_list RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "./*.cpp")
# Remove fuzz.cpp and util.cpp as they don't contain fuzz harnesses
list(REMOVE_ITEM fuzz_harness_file_list "fuzz.cpp")
list(REMOVE_ITEM fuzz_harness_file_list "util.cpp")
foreach(harness_file ${fuzz_harness_file_list})
get_filename_component(harness ${harness_file} NAME_WLE)
add_executable(fuzz_${harness} ${harness_file})
target_compile_definitions(fuzz_${harness} PUBLIC SINGLE_FUZZ_HARNESS)
target_link_libraries(fuzz_${harness}
bitcoin_crypto # TODO ??? memory_cleanse undefined errors
core_interface
test_fuzz
bitcoin_cli
bitcoin_common
minisketch
leveldb
univalue
secp256k1
Boost::headers
libevent::libevent
bitcoin_consensus
)
endforeach() |
Closes #28971
CPPFLAGS="-DFUZZ_HARNESS=<harness name>"The cumulative size of the individual binaries (compiled with LTO) is 3.6GB vs 14GB when search and replacing
std::getenv("FUZZ")(like we do in oss-fuzz).TODOs:
Makefile.test.include)test/fuzz/{tx_pool, tx_package_eval, deserialize}.cpp