Skip to content

implicit-integer-sign-change in ActivateSnapshot #30514

@maflcko

Description

@maflcko
$ echo 'dXR4b/8BAPq/tdph' | base64 --decode > ./crash

$ 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
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 2946336184
INFO: Loaded 1 modules   (625551 inline 8-bit counters): 625551 [0x572ffe28af78, 0x572ffe323b07), 
INFO: Loaded 1 PC tables (625551 PCs): 625551 [0x572ffe323b08,0x572ffecaf3f8), 
./src/test/fuzz/fuzz: Running 1 inputs 1 time(s) each.
Running: ./crash
validation.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)
    #0 0x572ffc2f8a69 in ChainstateManager::ActivateSnapshot(AutoFile&, node::SnapshotMetadata const&, bool) src/validation.cpp:5657:28
    #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
    #2 0x572ffb5b6896 in (anonymous namespace)::utxo_snapshot_fuzz_target(std::span<unsigned char const, 18446744073709551615ul>) src/test/fuzz/utxo_snapshot.cpp:96:9
    #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
    #4 0x572ffb73ac5d in LLVMFuzzerTestOneInput src/test/fuzz/fuzz.cpp:209:5

SUMMARY: 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:

diff --git a/test/functional/feature_assumeutxo.py b/test/functional/feature_assumeutxo.py
index 943862f8cf..fcb894c924 100755
--- a/test/functional/feature_assumeutxo.py
+++ b/test/functional/feature_assumeutxo.py
@@ -113,10 +113,10 @@ class AssumeutxoTest(BitcoinTestFramework):
         bogus_block_hash = "0" * 64  # Represents any unknown block hash
         # The height is not used for anything critical currently, so we just
         # confirm the manipulation in the error message
-        bogus_height = 1337
+        bogus_height = -1019704620
         for bad_block_hash in [bogus_block_hash, prev_block_hash]:
             with open(bad_snapshot_path, 'wb') as f:
-                f.write(valid_snapshot_contents[:11] + bogus_height.to_bytes(4, "little") + bytes.fromhex(bad_block_hash)[::-1] + valid_snapshot_contents[47:])
+                f.write(valid_snapshot_contents[:11] + bogus_height.to_bytes(4, "little", signed=True) + bytes.fromhex(bad_block_hash)[::-1] + valid_snapshot_contents[47:])
 
             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."
             assert_raises_rpc_error(-32603, msg, node.loadtxoutset, bad_snapshot_path)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions