0$ echo 'dXR4b/8BAPq/tdph' | base64 --decode > ./crash
1
2$ UBSAN_OPTIONS="suppressions=$PWD/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1:report_error_type=1" FUZZ=utxo_snapshot ./src/test/fuzz/fuzz ./crash
3INFO: Running with entropic power schedule (0xFF, 100).
4INFO: Seed: 2946336184
5INFO: Loaded 1 modules (625551 inline 8-bit counters): 625551 [0x572ffe28af78, 0x572ffe323b07),
6INFO: Loaded 1 PC tables (625551 PCs): 625551 [0x572ffe323b08,0x572ffecaf3f8),
7./src/test/fuzz/fuzz: Running 1 inputs 1 time(s) each.
8Running: ./crash
9validation.cpp:5657:28: runtime error: implicit conversion from type 'uint32_t' (aka 'unsigned int') of value 3275262676 (32-bit, unsigned) to type 'int' changed the value to -1019704620 (32-bit, signed)
10 [#0](/bitcoin-bitcoin/0/) 0x572ffc2f8a69 in ChainstateManager::ActivateSnapshot(AutoFile&, node::SnapshotMetadata const&, bool) src/validation.cpp:5657:28
11 [#1](/bitcoin-bitcoin/1/) 0x572ffb5b93ac in (anonymous namespace)::utxo_snapshot_fuzz_target(std::span<unsigned char const, 18446744073709551615ul>)::$_0::operator()() const src/test/fuzz/utxo_snapshot.cpp:83:27
12 [#2](/bitcoin-bitcoin/2/) 0x572ffb5b6896 in (anonymous namespace)::utxo_snapshot_fuzz_target(std::span<unsigned char const, 18446744073709551615ul>) src/test/fuzz/utxo_snapshot.cpp:96:9
13 [#3](/bitcoin-bitcoin/3/) 0x572ffb73ac5d in std::function<void (std::span<unsigned char const, 18446744073709551615ul>)>::operator()(std::span<unsigned char const, 18446744073709551615ul>) const /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_function.h:591:9
14 [#4](/bitcoin-bitcoin/4/) 0x572ffb73ac5d in LLVMFuzzerTestOneInput src/test/fuzz/fuzz.cpp:209:5
15
16SUMMARY: UndefinedBehaviorSanitizer: implicit-integer-sign-change validation.cpp:5657:28
The reason is that metadata is untrusted input, but it isn’t sanitized at that point. I presume it can also be reproduced with the integer sanitizer and:
0diff --git a/test/functional/feature_assumeutxo.py b/test/functional/feature_assumeutxo.py
1index 943862f8cf..fcb894c924 100755
2--- a/test/functional/feature_assumeutxo.py
3+++ b/test/functional/feature_assumeutxo.py
4@@ -113,10 +113,10 @@ class AssumeutxoTest(BitcoinTestFramework):
5 bogus_block_hash = "0" * 64 # Represents any unknown block hash
6 # The height is not used for anything critical currently, so we just
7 # confirm the manipulation in the error message
8- bogus_height = 1337
9+ bogus_height = -1019704620
10 for bad_block_hash in [bogus_block_hash, prev_block_hash]:
11 with open(bad_snapshot_path, 'wb') as f:
12- f.write(valid_snapshot_contents[:11] + bogus_height.to_bytes(4, "little") + bytes.fromhex(bad_block_hash)[::-1] + valid_snapshot_contents[47:])
13+ f.write(valid_snapshot_contents[:11] + bogus_height.to_bytes(4, "little", signed=True) + bytes.fromhex(bad_block_hash)[::-1] + valid_snapshot_contents[47:])
14
15 msg = f"Unable to load UTXO snapshot: assumeutxo block hash in snapshot metadata not recognized (hash: {bad_block_hash}, height: {bogus_height}). The following snapshot heights are available: 110, 200, 299."
16 assert_raises_rpc_error(-32603, msg, node.loadtxoutset, bad_snapshot_path)