Skip to content

Conversation

Crypt-iQ
Copy link
Contributor

@Crypt-iQ Crypt-iQ commented Aug 13, 2020

First steps towards running fuzz tests with -fsanitize=integer and not having it fail. This PR fixes the complaints in the src/test/fuzz directory only. There are other complaints that are not handled because I thought the scope of the PR would sprawl.

c0f40ba fixes

SUMMARY: UndefinedBehaviorSanitizer: implicit-integer-sign-change test/fuzz/FuzzedDataProvider.h:108:27 in
test/fuzz/FuzzedDataProvider.h:87:51

SUMMARY: UndefinedBehaviorSanitizer: unsigned-integer-overflow test/fuzz/FuzzedDataProvider.h:108:31 in
test/fuzz/FuzzedDataProvider.h:108:31

SUMMARY: UndefinedBehaviorSanitizer: implicit-integer-sign-change /usr/local/opt/llvm/bin/../include/c++/v1/memory:1876:35

03826e5 fixes

SUMMARY: UndefinedBehaviorSanitizer: unsigned-integer-overflow test/fuzz/FuzzedDataProvider.h:108:31 in
test/fuzz/util.h:166:35

SUMMARY: UndefinedBehaviorSanitizer: implicit-integer-sign-change test/fuzz/FuzzedDataProvider.h:108:27 in
test/fuzz/script.cpp:145:37

febe0f8 fixes

SUMMARY: UndefinedBehaviorSanitizer: implicit-integer-sign-change test/fuzz/FuzzedDataProvider.h:108:27 in
test/fuzz/crypto_chacha20_poly1305_aead.cpp:63:25

d698ead fixes

test/fuzz/pow.cpp:46:39: runtime error: implicit conversion from type 'long' of value 4294967712 (64-bit, signed) to type 'uint32_t' (aka 'unsigned int')      changed the value to 416 (32-bit, unsigned)

Copy link
Contributor

@practicalswift practicalswift left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested ACK 8fdf71c

Thanks a lot for improving our fuzzing harness @Crypt-iQ. Love it!

@practicalswift
Copy link
Contributor

practicalswift commented Aug 14, 2020

@Crypt-iQ While you're at it touching crypto_chacha20_poly1305_aead.cpp, would you mind fixing this one too? :)

test/fuzz/crypto_chacha20_poly1305_aead.cpp:50:27: runtime error: unsigned integer overflow: 18446744073709551615 + 1 cannot be represented in type 'unsigned long'

(Line 50 is after applying this patch.)

Base64 encoded test case:

AOcDAAAKI0q6uhoA/wAKf////xUVFf//AAAAAABKFRUVFRUVFQoVFRUVFRUVFRUVFRUbBhUVFQAVFUkAAAAOABoA/wAKf/////8B///////////////////k/////////0AbBg==

@Crypt-iQ
Copy link
Contributor Author

There is one other complaint in the fuzz directory:

if (fuzzed_data_provider.ConsumeBool()) {
current_block.nTime = fixed_time + current_height * consensus_params.nPowTargetSpacing;
}

test/fuzz/pow.cpp:46:39: runtime error: implicit conversion from type 'long' of value 4294967712 (64-bit, signed) to type 'uint32_t' (aka 'unsigned int')      changed the value to 416 (32-bit, unsigned)

Not entirely sure what value to set current_block.nTime to in that scenario. Also it can overflow via multiplication or addition.

@practicalswift
Copy link
Contributor

Not entirely sure what value to set current_block.nTime to in that scenario. Also it can overflow via multiplication or addition.

Perhaps you can use MultiplicationOverflow and AdditionOverflow (both in test/fuzz/util.h) to guard against the overflows? :)

@Crypt-iQ Crypt-iQ force-pushed the fuzz_supp_0813 branch 2 times, most recently from 3c4a4ad to 39a2f5d Compare August 18, 2020 11:33
@Crypt-iQ
Copy link
Contributor Author

Crypt-iQ commented Aug 18, 2020

Get this complaint on macOS Catalina v10.15.4 with clang version 10.0.1 (installed via brew install llvm):

/usr/local/Cellar/llvm/10.0.1/bin/../include/c++/v1/memory:1876:35: runtime error: implicit conversion from type 'char' of value -41 (8-bit, signed) to type 'unsigned char' changed the value to 215 (8-bit, unsigned)
    #0 0x1075d43ff in std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >::vector<std::__1::__wrap_iter<char const*> >(std::__1::__wrap_iter<char const*>, std::__1::enable_if<(__is_cpp17_forward_iterator<std::__1::__wrap_iter<char const*> >::value) && (is_constructible<unsigned char, std::__1::iterator_traits<std::__1::__wrap_iter<char const*> >::reference>::value), std::__1::__wrap_iter<char const*> >::type) vector:1223
    #1 0x1075d3656 in std::__1::optional<CBlockHeader> ConsumeDeserializable<CBlockHeader>(FuzzedDataProvider&, unsigned long) util.h:70
    #2 0x1075d1867 in test_one_input(std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > const&) pow.cpp:31
    #3 0x107b13429 in LLVMFuzzerTestOneInput fuzz.cpp:45
    #4 0x107be4d00 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) FuzzerLoop.cpp:556
    #5 0x107be4445 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) FuzzerLoop.cpp:470
    #6 0x107be6ae7 in fuzzer::Fuzzer::ReadAndExecuteSeedCorpora(std::__1::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) FuzzerLoop.cpp:765
    #7 0x107be6e49 in fuzzer::Fuzzer::Loop(std::__1::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) FuzzerLoop.cpp:792
    #8 0x107bd413d in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) FuzzerDriver.cpp:829
    #9 0x107c00572 in main FuzzerMain.cpp:19
    #10 0x7fff7114bcc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)

Can't reproduce on my ubuntu digitalocean box with clang-10 and am wondering if anybody else with a Mac can reproduce.

@adaminsky
Copy link
Contributor

I can reproduce this on macOS 10.15.5 with clang installed from brew install llvm.

@Crypt-iQ
Copy link
Contributor Author

@adaminsky what are the options you pass to configure?

@adaminsky
Copy link
Contributor

@Crypt-iQ I used ./configure --enable-fuzz --with-sanitizers=fuzzer,integer CC=/usr/local/opt/llvm/bin/clang CXX=/usr/local/opt/llvm/bin/clang++ --disable-asm.

@DrahtBot
Copy link
Contributor

DrahtBot commented Aug 20, 2020

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

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.

@Crypt-iQ
Copy link
Contributor Author

Crypt-iQ commented Aug 22, 2020

@adaminsky If you also run with:

UBSAN_OPTIONS="suppressions=test/sanitizer_suppressions/ubsan:print_stacktrace=1:report_error_type=1" src/test/fuzz/pow ~/qa-assets/fuzz_seed_corpus/pow

Do you also get the following complaint?

/usr/local/opt/llvm/bin/../include/c++/v1/memory:1876:35: runtime error: implicit conversion from type 'unsigned char' of value 215 (8-bit, unsigned) to type 'char' changed the value to -41 (8-bit, signed)
    #0 0x1029da360 in void std::__1::allocator_traits<zero_after_free_allocator<char> >::__construct<char, unsigned char const&>(std::__1::integral_constant<bool, true>, zero_after_free_allocator<char>&, char*, unsigned char const&) memory:1768
    #1 0x1029d9e95 in void std::__1::allocator_traits<zero_after_free_allocator<char> >::__construct_range_forward<std::__1::__wrap_iter<unsigned char const*>, char*>(zero_after_free_allocator<char>&, std::__1::__wrap_iter<unsigned char const*>, std::__1::__wrap_iter<unsigned char const*>, char*&) memory:1688
    #2 0x1029d8a78 in std::__1::enable_if<__is_cpp17_forward_iterator<std::__1::__wrap_iter<unsigned char const*> >::value, void>::type std::__1::vector<char, zero_after_free_allocator<char> >::__construct_at_end<std::__1::__wrap_iter<unsigned char const*> >(std::__1::__wrap_iter<unsigned char const*>, std::__1::__wrap_iter<unsigned char const*>, unsigned long) vector:1076
    #3 0x1029d8642 in std::__1::vector<char, zero_after_free_allocator<char> >::vector<std::__1::__wrap_iter<unsigned char const*> >(std::__1::__wrap_iter<unsigned char const*>, std::__1::enable_if<(__is_cpp17_forward_iterator<std::__1::__wrap_iter<unsigned char const*> >::value) && (is_constructible<char, std::__1::iterator_traits<std::__1::__wrap_iter<unsigned char const*> >::reference>::value), std::__1::__wrap_iter<unsigned char const*> >::type) vector:1223
    #4 0x1029d830f in CDataStream::CDataStream(std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > const&, int, int) streams.h:247
    #5 0x1029ccaf2 in std::__1::optional<CBlockHeader> ConsumeDeserializable<CBlockHeader>(FuzzedDataProvider&, unsigned long) util.h:71
    #6 0x1029cbb1a in test_one_input(std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > const&) pow.cpp:31
    #7 0x103808f30 in LLVMFuzzerTestOneInput fuzz.cpp:45
    #8 0x1039e10d0 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) FuzzerLoop.cpp:556
    #9 0x1039e0815 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) FuzzerLoop.cpp:470
    #10 0x1039e2eb7 in fuzzer::Fuzzer::ReadAndExecuteSeedCorpora(std::__1::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) FuzzerLoop.cpp:765
    #11 0x1039e3219 in fuzzer::Fuzzer::Loop(std::__1::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) FuzzerLoop.cpp:792
    #12 0x1039d06dd in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) FuzzerDriver.cpp:829
    #13 0x1039fc3b2 in main FuzzerMain.cpp:19
    #14 0x7fff73778cc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)

It hits util.h:71 instead of util.h:70

@Crypt-iQ
Copy link
Contributor Author

These two complaints happen because char is signed by default with our Mac setups. Ubuntu box reports no errors.

@Crypt-iQ
Copy link
Contributor Author

Crypt-iQ commented Aug 23, 2020

The first complaint happens because of the conversion from std::string (which is std::basic_string<char>) to std::vector<uint8_t> here:

NODISCARD inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
{
const std::string s = fuzzed_data_provider.ConsumeRandomLengthString(max_length);
return {s.begin(), s.end()};
}

The second complaint happens because a std::vector<uint8_t> is passed into CDataStream here:

const std::vector<uint8_t> buffer = ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length);
CDataStream ds{buffer, SER_NETWORK, INIT_PROTO_VERSION};

and CDataStream uses a byte-vector of char:

// Byte-vector that clears its contents before deletion.
typedef std::vector<char, zero_after_free_allocator<char> > CSerializeData;

Curious as to your thoughts about these @practicalswift. In the first example, perhaps ConsumeRandomLengthString could return std::basic_string<unsigned char>. Haven't figured out what to do about the second example.

@adaminsky
Copy link
Contributor

@Crypt-iQ Confirming that I can reproduce both complaints and that char is signed for me.

Copy link
Member

@darosior darosior left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK.

In the first example, perhaps ConsumeRandomLengthString could return std::basic_string. Haven't figured out what to do about the second example.

ConsumeRandomLengthString is part of LLVM's header i don't think it's a good idea to modify it.

Regarding the second complaint, you could directly consume a string without the cast to uint8_t from ConsumeRandomLengthByteVector:

index ae47a2805..fb89dc713 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -38,9 +38,15 @@ NODISCARD inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataPr
     return {s.begin(), s.end()};
 }
 
+NODISCARD inline std::vector<char> ConsumeRandomLengthCharVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
+{
+    const std::string s = fuzzed_data_provider.ConsumeRandomLengthString(max_length);
+    return {s.begin(), s.end()};
+}
+
 NODISCARD inline CDataStream ConsumeDataStream(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
 {
-    return {ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length), SER_NETWORK, INIT_PROTO_VERSION};
+    return {ConsumeRandomLengthCharVector(fuzzed_data_provider, max_length), SER_NETWORK, INIT_PROTO_VERSION};
 }
 
 NODISCARD inline std::vector<std::string> ConsumeRandomLengthStringVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_vector_size = 16, const size_t max_string_length = 16) noexcept

Regarding the first complaint, ConsumeBytes:

// Returns a std::vector containing |num_bytes| of input data. If fewer than
  // |num_bytes| of data remain, returns a shorter std::vector containing all
  // of the data that's left. Can be used with any byte sized type, such as
  // char, unsigned char, uint8_t, etc.
  template <typename T> std::vector<T> ConsumeBytes(size_t num_bytes) {
    num_bytes = std::min(num_bytes, remaining_bytes_);
    return ConsumeBytes<T>(num_bytes, num_bytes);
  }

Is almost what you want, as it's just missing the "random length" part. Maybe our ConsumeRandomLengthByteVector could use a combination of this one combined with ConsumeRandomLengthString's randomness logic ?

@Crypt-iQ Crypt-iQ force-pushed the fuzz_supp_0813 branch 2 times, most recently from 782cd34 to e677639 Compare August 28, 2020 16:46
Copy link
Contributor

@practicalswift practicalswift left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some additional review :)

Since -fsanitize=integer's unsigned-integer-overflow and implicit-integer-sign-change are not UB (but could be indications of bugs) I think it makes sense to be somewhat liberal with tackling these cases by adding suppressions instead of harness changes. Rationales given in each comment.

…ppressions

This is a first step towards getting -fsanitize=integer to not report
complaints when fuzzing. Without this, the implicit-integer-sign-change
and unsigned-integer-overflow checks are triggered for pretty much
every fuzz target. A suppression is added for memory* since macOS
builds can trigger implicit-integer-sign-change when using either
ConsumeRandomLengthByteVector or ConsumeDeserializable.
This commit also uses AdditionOverflow to prevent overflow.
@Crypt-iQ Crypt-iQ changed the title fuzz: fix -fsanitize=integer complaints tests: fix -fsanitize=integer complaints Sep 1, 2020
Copy link
Contributor

@practicalswift practicalswift left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK d698ead -- patch looks correct

@DrahtBot
Copy link
Contributor

🐙 This pull request conflicts with the target branch and needs rebase.

Want to unsubscribe from rebase notifications on this pull request? Just convert this pull request to a "draft".

@Crypt-iQ
Copy link
Contributor Author

Closed by #21000

@Crypt-iQ Crypt-iQ closed this Feb 14, 2021
@Crypt-iQ Crypt-iQ deleted the fuzz_supp_0813 branch February 14, 2021 15:46
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Aug 18, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants