Add fuzzing harness for TorController
.
See doc/fuzzing.md
for information on how to fuzz Bitcoin Core. Don’t forget to contribute any coverage increasing inputs you find to the Bitcoin Core fuzzing corpus repo.
Happy fuzzing :)
Add fuzzing harness for TorController
.
See doc/fuzzing.md
for information on how to fuzz Bitcoin Core. Don’t forget to contribute any coverage increasing inputs you find to the Bitcoin Core fuzzing corpus repo.
Happy fuzzing :)
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
Reviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
54+ }
55+ tor_control_reply.lines = ConsumeRandomLengthStringVector(fuzzed_data_provider);
56+ if (tor_control_reply.lines.empty()) {
57+ break;
58+ }
59+ DummyTorControlConnection dummy_tor_control_connection;
TorControlConnection tor_control_connection{nullptr};
and then you can get rid of the DummyTorControlConnection
class?
TorControlConnection::{Connect,Disconnect,Command}
to be called, right? :) My intention with adding DummyTorControlConnection
was to make sure {Connect,Disconnect,Command}
return immediately (and thus be guaranteed to be safe regardless of the state of things).
DummyTorControlConnection
as unused.
48 hour coverage with libfuzzer here: https://crypt-iq.github.io/torcontrol_cov/src/torcontrol.cpp.gcov.html
Currently two functions are coverage lacking from the above output:
authchallenge_cb
protocolinfo_cb
authchallenge_cb
coverage is a bit tricky to get coverage for because it has to pass the HMAC check. protocolinfo_cb
just needs better seeds and it should be able to hit every part but this one:
https://github.com/bitcoin/bitcoin/blob/9d4b3d86b694ac6e56495e1955f6bf5ff584cbb9/src/torcontrol.cpp#L643-L652
Runs without complaint on Ubuntu 18:
./configure --enable-fuzz --with-sanitizers=address,undefined,fuzzer CC=clang-10 CXX=clang++-10
Will build on macOS and report back.
Builds on macOS:
./configure --enable-fuzz --with-sanitizers=fuzzer,address,undefined
no actionable complaints
./configure --enable-fuzz --with-sanitizers=fuzzer,integer,undefined
no actionable complaints
Ran with Ubuntu 18 again:
./configure --enable-fuzz --with-sanitizers=fuzzer,integer
:
0/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/bits/basic_string.tcc:1271:25: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::size_type' (aka 'unsigned long')
1[#0](/bitcoin-bitcoin/0/) 0x55db7101bde3 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::rfind(char, unsigned long) const /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/bits/basic_string.tcc:1271:25
2[#1](/bitcoin-bitcoin/1/) 0x55db7101bde3 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::find_last_of(char, unsigned long) const /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/bits/basic_string.h:2636:22
3[#2](/bitcoin-bitcoin/2/) 0x55db7101bde3 in SplitHostPort(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) /root/bitcoin/src/util/strencodings.cpp:111:23
4[#3](/bitcoin-bitcoin/3/) 0x55db70fc30f6 in Lookup(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<CService, std::allocator<CService> >&, int, bool, unsigned int) /root/bitcoin/src/netbase.cpp:213:5
5[#4](/bitcoin-bitcoin/4/) 0x55db70fc36ca in Lookup(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, CService&, int, bool) /root/bitcoin/src/netbase.cpp:237:17
6[#5](/bitcoin-bitcoin/5/) 0x55db70fc389b in LookupNumeric(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int) /root/bitcoin/src/netbase.cpp:262:9
7[#6](/bitcoin-bitcoin/6/) 0x55db70ec5bfa in TorController::auth_cb(TorControlConnection&, TorControlReply const&) /root/bitcoin/src/torcontrol.cpp:411:31
8[#7](/bitcoin-bitcoin/7/) 0x55db70ebb54b in test_one_input(std::vector<unsigned char, std::allocator<unsigned char> > const&) /root/bitcoin/src/test/fuzz/torcontrol.cpp:66:28
9[#8](/bitcoin-bitcoin/8/) 0x55db71583916 in LLVMFuzzerTestOneInput /root/bitcoin/src/test/fuzz/fuzz.cpp:45:5
10[#9](/bitcoin-bitcoin/9/) 0x55db70e4b641 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/root/bitcoin/src/test/fuzz/torcontrol+0x649641)
11[#10](/bitcoin-bitcoin/10/) 0x55db70e4ad85 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) (/root/bitcoin/src/test/fuzz/torcontrol+0x648d85)
12[#11](/bitcoin-bitcoin/11/) 0x55db70e4d6a7 in fuzzer::Fuzzer::ReadAndExecuteSeedCorpora(std::__Fuzzer::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) (/root/bitcoin/src/test/fuzz/torcontrol+0x64b6a7)
13[#12](/bitcoin-bitcoin/12/) 0x55db70e4da09 in fuzzer::Fuzzer::Loop(std::__Fuzzer::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) (/root/bitcoin/src/test/fuzz/torcontrol+0x64ba09)
14[#13](/bitcoin-bitcoin/13/) 0x55db70e3c6de in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/root/bitcoin/src/test/fuzz/torcontrol+0x63a6de)
15[#14](/bitcoin-bitcoin/14/) 0x55db70e65522 in main (/root/bitcoin/src/test/fuzz/torcontrol+0x663522)
16[#15](/bitcoin-bitcoin/15/) 0x7f13c3c4fb96 in __libc_start_main /build/glibc-2ORdQG/glibc-2.27/csu/../csu/libc-start.c:310
17[#16](/bitcoin-bitcoin/16/) 0x55db70e11459 in _start (/root/bitcoin/src/test/fuzz/torcontrol+0x60f459)
18SUMMARY: UndefinedBehaviorSanitizer: unsigned-integer-overflow /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/bits/basic_string.tcc:1271:25
-fsanitize=integer
suppressions (all non-UB). Please re-review :)
77+implicit-integer-sign-change:util/strencodings.cpp
78+implicit-integer-sign-change:util/strencodings.h
79 implicit-integer-sign-change:validation.cpp
80 implicit-integer-sign-change:zmq/zmqpublishnotifier.cpp
81-implicit-signed-integer-truncation,implicit-integer-sign-change:chain.h
82-implicit-signed-integer-truncation,implicit-integer-sign-change:test/skiplist_tests.cpp
chain.h
& test/skiplist_tests.cpp
missing the implicit-signed-integer-truncation
suppression in the new version. Then CI should pass.
11@@ -12,6 +12,7 @@ float-divide-by-zero:wallet/wallet.cpp
12 # contains files in which we expect unsigned integer overflows to occur. The
13 # list is used to suppress -fsanitize=integer warnings when running our CI UBSan
14 # job.
15+unsigned-integer-overflow:*/include/c++/*/bits/basic_string.tcc
basic_string.tcc
. I think that suppressing them is fine: unsigned integer wraparound is not UB after all :)
Had to rebase again due to a merge conflict. @Crypt-iQ Again: thanks a lot for your efforts in reviewing the fuzzing harnesses. May I ask you a re-review and hopefully give your fourth(!) ACK for this change? :)
Fuzzing review beg: If we only had two or three more reviewers (like @Crypt-iQ!) who were willing to review fuzzing harnesses that would make such a huge difference for the state of fuzzing in the project. Please consider reviewing! ❤️
Some fuzzing harnesses awaiting review:
ACK 00e48011c3e1235077353ca7d209661de66e9aab
Reviewed code and ran locally on macOS 10.15.4 with clang-10.
57@@ -56,6 +58,7 @@ implicit-integer-sign-change:script/interpreter.cpp
58 implicit-integer-sign-change:serialize.h
59 implicit-integer-sign-change:test/arith_uint256_tests.cpp
60 implicit-integer-sign-change:test/coins_tests.cpp
61+implicit-integer-sign-change:test/fuzz/FuzzedDataProvider.h
FuzzedDataProvider.h
to the most recent version. Suppression removed! :)
test_runner.py
on macOS, clang-11, --with-sanitizers=fuzzer,undefined,integer
, commit 6d81d7aa87a15edd87175c33d1fd4a2accb3549d:
0test/fuzz/FuzzedDataProvider.h:211:49: runtime error: implicit conversion from type 'int' of value -2147483648 (32-bit, signed) to type 'unsigned long long' changed the value to 18446744071562067968 (64-bit, unsigned)
1SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior test/fuzz/FuzzedDataProvider.h:211:49 in
2test/fuzz/FuzzedDataProvider.h:211:47: runtime error: unsigned integer overflow: 2147483647 - 18446744071562067968 cannot be represented in type 'unsigned long long'
3SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior test/fuzz/FuzzedDataProvider.h:211:47 in
55+ tor_control_reply.lines = ConsumeRandomLengthStringVector(fuzzed_data_provider);
56+ if (tor_control_reply.lines.empty()) {
57+ break;
58+ }
59+ DummyTorControlConnection dummy_tor_control_connection;
60+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 3)) {
49+ tor_control_reply.code = 250;
50+ } else if (fuzzed_data_provider.ConsumeBool()) {
51+ tor_control_reply.code = 510;
52+ } else {
53+ tor_control_reply.code = fuzzed_data_provider.ConsumeIntegral<int>();
54+ }
nit: Are the constants from our source code or from somewhere else? Like https://src-ref.docs.torproject.org/tor/control__cmd_8c_source.html ?
Not sure, but if you want, you can also use CallOneOf
here:
0 CallOneOf (fuzzed_data_provider,
1 [&] {
2 tor_control_reply.code = 250;
3 } ,
4 [&]{
5 tor_control_reply.code = 510;
6 } ,
7 [&]{
8 tor_control_reply.code = fuzzed_data_provider.ConsumeIntegral<int>();
9 });
Good point. Now using CallOneOf
.
250
and 510
are from our source:
0$ git grep reply.code
1src/test/fuzz/torcontrol.cpp: tor_control_reply.code = 250;
2src/test/fuzz/torcontrol.cpp: tor_control_reply.code = 510;
3src/test/fuzz/torcontrol.cpp: tor_control_reply.code = fuzzed_data_provider.ConsumeIntegral<int>();
4src/torcontrol.cpp: if (reply.code == 250) {
5src/torcontrol.cpp: } else if (reply.code == 510) { // 510 Unrecognized command
6src/torcontrol.cpp: LogPrintf("tor: Add onion failed; error code %d\n", reply.code);
7src/torcontrol.cpp: if (reply.code == 250) {
8src/torcontrol.cpp: if (reply.code == 250) {
9src/torcontrol.cpp: if (reply.code == 250) {
Updated to use the excellent CallOneOf
!
Should be ready for final review :)
Compared to other fuzzing PRs this PR is relatively cumbersome to keep up to date over time due to the code move from torcontrol.cpp
to torcontrol.h
which must be kept in sync. Final review welcome :)
Current status:
Concept ACK
ACKs
The first commit which is move-only is best viewed dimmed 🦓 style:
0$ git show --color-moved=dimmed_zebra bb643af8a1bc207935e9ae2c0fe61378ee935bde
This PR has one stale ACK and three Concept ACKs, and I believe all feedback has been addressed.
Ready for merge after re-review?
Sorry, forgot about this a bit. I’ll have a look.
Looks like this has a silent merge conflict on current master:
0…/bitcoin/src/test/fuzz/torcontrol.cpp:38:39: error: use of undeclared identifier 'MakeFuzzingContext'
1 static const auto testing_setup = MakeFuzzingContext<>();
2 ^
31 error generated.
Looks like this has a silent merge conflict on current master:
0…/bitcoin/src/test/fuzz/torcontrol.cpp:38:39: error: use of undeclared identifier 'MakeFuzzingContext' 1 static const auto testing_setup = MakeFuzzingContext<>(); 2 ^ 31 error generated.
Oh, thanks for noticing!
Now addressed :)
I want to say this one more time before merging: I dislike moving internal implementation details to the header for testing purposes. It kind of violates encapsulation and makes the headers larger and less usefully self-documenting.
That said, I do not see any other solution in the immediate context here. A longer-term direction would be to have test-specific headers that expose internal functionality for unit testing[, benching, fuzzing] but are never included in production code.
ACK 10d4477dae663631411a1bd5a92e4fa941d3a96c