Skip to content

Commit a8a272a

Browse files
author
merge-script
committed
Merge bitcoin/bitcoin#22734: addrman: Avoid crash on corrupt data, Force Check after deserialize
fa3669f fuzz: Move all addrman fuzz targets to one file (MarcoFalke) fa7a883 addrman: Replace assert with throw on corrupt data (MarcoFalke) fa29897 Refactor: Turn the internal addrman check helper into a forced check (MarcoFalke) fae5c63 move-only: Move CAddrMan::Check to cpp file (MarcoFalke) Pull request description: Assert should only be used for program internal logic errors, not to sanitize external user input. The assert was introduced via the debug-only runtime option `-checkaddrman` in commit 803ef70, thus won't need a backport. Also, it doesn't really make sense to continue when the deserialized addrman doesn't pass the sanity check. For example, if `nLastSuccess` is negative, it would later result in integer overflows. Thus, this patch fixes #22931. Also, Fixes #22503 Fixes #22504 Fixes #22519 Closes #22498 Steps to test: ``` mkdir -p /tmp/test_235/regtest/ echo 'H4sIAAAAAAAAA/u1f+stZmUGYgELgwPRakfBKBgFo2AUjIJRMApGwSgYBaNgFIyCUTBswdyGpFnLjUKjP9e0bvjYusl6b+L2e7Vs2dd6N//Pua0/xQUALJAn93IQAAA=' | base64 --decode | zcat > /tmp/test_235/regtest/peers.dat ./src/qt/bitcoin-qt -regtest -datadir=/tmp/test_235/ -checkaddrman=1 -printtoconsole | grep -A2 'Loading P2P addresses' ``` Output before: ``` 2021-09-10T11:28:37Z init message: Loading P2P addresses… 2021-09-10T11:28:37Z ADDRMAN CONSISTENCY CHECK FAILED!!! err=-16 bitcoin-qt: addrman.cpp:765: void CAddrMan::Check() const: Assertion `false' failed. (program crashes) ``` Output after: ``` 2021-09-10T11:26:00Z init message: Loading P2P addresses… 2021-09-10T11:26:00Z Error: Invalid or corrupt peers.dat (Corrupt data. Consistency check failed with code -16: iostream error). If you believe this is a bug, please report it to https://github.com/bitcoin/bitcoin/issues. As a workaround, you can move the file ("/tmp/test_235/regtest/peers.dat") out of the way (rename, move, or delete) to have a new one created on the next start. (program exits) ``` ACKs for top commit: naumenkogs: ACK fa3669f jnewbery: Code review ACK fa3669f vasild: ACK fa3669f Tree-SHA512: 687e4a4765bbc66495152fa7a49d28ee84b405dc5370ba87b4016b5593e45f54c4ce5cae579e4d433e0e082d20fc263969fa602679c911accef0adb2d6213bd6
2 parents ae674a0 + fa3669f commit a8a272a

File tree

6 files changed

+46
-49
lines changed

6 files changed

+46
-49
lines changed

src/Makefile.test.include

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,6 @@ test_fuzz_fuzz_SOURCES = \
232232
test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp \
233233
test/fuzz/crypto_poly1305.cpp \
234234
test/fuzz/cuckoocache.cpp \
235-
test/fuzz/data_stream.cpp \
236235
test/fuzz/decode_tx.cpp \
237236
test/fuzz/descriptor_parse.cpp \
238237
test/fuzz/deserialize.cpp \

src/addrman.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,12 @@ void CAddrMan::Unserialize(Stream& s_)
386386
LogPrint(BCLog::ADDRMAN, "addrman lost %i new and %i tried addresses due to collisions or invalid addresses\n", nLostUnk, nLost);
387387
}
388388

389-
Check();
389+
const int check_code{ForceCheckAddrman()};
390+
if (check_code != 0) {
391+
throw std::ios_base::failure(strprintf(
392+
"Corrupt data. Consistency check failed with code %s",
393+
check_code));
394+
}
390395
}
391396

392397
// explicit instantiation
@@ -743,13 +748,24 @@ CAddrInfo CAddrMan::Select_(bool newOnly) const
743748
}
744749
}
745750

746-
int CAddrMan::Check_() const
751+
void CAddrMan::Check() const
747752
{
748753
AssertLockHeld(cs);
749754

750755
// Run consistency checks 1 in m_consistency_check_ratio times if enabled
751-
if (m_consistency_check_ratio == 0) return 0;
752-
if (insecure_rand.randrange(m_consistency_check_ratio) >= 1) return 0;
756+
if (m_consistency_check_ratio == 0) return;
757+
if (insecure_rand.randrange(m_consistency_check_ratio) >= 1) return;
758+
759+
const int err{ForceCheckAddrman()};
760+
if (err) {
761+
LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
762+
assert(false);
763+
}
764+
}
765+
766+
int CAddrMan::ForceCheckAddrman() const
767+
{
768+
AssertLockHeld(cs);
753769

754770
LogPrint(BCLog::ADDRMAN, "Addrman checks started: new %i, tried %i, total %u\n", nNew, nTried, vRandom.size());
755771

src/addrman.h

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -391,20 +391,12 @@ class CAddrMan
391391
//! Return a random to-be-evicted tried table address.
392392
CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
393393

394-
//! Consistency check
395-
void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs)
396-
{
397-
AssertLockHeld(cs);
398-
399-
const int err = Check_();
400-
if (err) {
401-
LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
402-
assert(false);
403-
}
404-
}
394+
//! Consistency check, taking into account m_consistency_check_ratio. Will std::abort if an inconsistency is detected.
395+
void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs);
405396

406-
//! Perform consistency check. Returns an error code or zero.
407-
int Check_() const EXCLUSIVE_LOCKS_REQUIRED(cs);
397+
//! Perform consistency check, regardless of m_consistency_check_ratio.
398+
//! @returns an error code or zero.
399+
int ForceCheckAddrman() const EXCLUSIVE_LOCKS_REQUIRED(cs);
408400

409401
/**
410402
* Return all or many randomly selected addresses, optionally by network.

src/test/fuzz/addrman.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ void initialize_addrman()
2323
SelectParams(CBaseChainParams::REGTEST);
2424
}
2525

26+
FUZZ_TARGET_INIT(data_stream_addr_man, initialize_addrman)
27+
{
28+
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
29+
CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
30+
CAddrMan addr_man(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
31+
try {
32+
ReadFromStream(addr_man, data_stream);
33+
} catch (const std::exception&) {
34+
}
35+
}
36+
2637
class CAddrManDeterministic : public CAddrMan
2738
{
2839
public:

src/test/fuzz/data_stream.cpp

Lines changed: 0 additions & 30 deletions
This file was deleted.

test/functional/feature_addrman.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def serialize_addrman(
1919
format=1,
2020
lowest_compatible=3,
2121
net_magic="regtest",
22+
bucket_key=1,
2223
len_new=None,
2324
len_tried=None,
2425
mock_checksum=None,
@@ -29,7 +30,7 @@ def serialize_addrman(
2930
r = MAGIC_BYTES[net_magic]
3031
r += struct.pack("B", format)
3132
r += struct.pack("B", INCOMPATIBILITY_BASE + lowest_compatible)
32-
r += ser_uint256(1)
33+
r += ser_uint256(bucket_key)
3334
r += struct.pack("i", len_new or len(new))
3435
r += struct.pack("i", len_tried or len(tried))
3536
ADDRMAN_NEW_BUCKET_COUNT = 1 << 10
@@ -119,6 +120,14 @@ def run_test(self):
119120
match=ErrorMatch.FULL_REGEX,
120121
)
121122

123+
self.log.info("Check that corrupt addrman cannot be read (failed check)")
124+
self.stop_node(0)
125+
write_addrman(peers_dat, bucket_key=0)
126+
self.nodes[0].assert_start_raises_init_error(
127+
expected_msg=init_error("Corrupt data. Consistency check failed with code -16: .*"),
128+
match=ErrorMatch.FULL_REGEX,
129+
)
130+
122131
self.log.info("Check that missing addrman is recreated")
123132
self.stop_node(0)
124133
os.remove(peers_dat)

0 commit comments

Comments
 (0)