Skip to content

Commit fa55c3d

Browse files
author
MarcoFalke
committed
Raise InitError when peers.dat is invalid or corrupted
1 parent fa4e2cc commit fa55c3d

File tree

5 files changed

+68
-54
lines changed

5 files changed

+68
-54
lines changed

src/addrdb.cpp

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121
#include <util/translation.h>
2222

2323
namespace {
24+
25+
class DbNotFoundError : public std::exception
26+
{
27+
using std::exception::exception;
28+
};
29+
2430
template <typename Stream, typename Data>
2531
bool SerializeDB(Stream& stream, const Data& data)
2632
{
@@ -78,47 +84,40 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
7884
}
7985

8086
template <typename Stream, typename Data>
81-
bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
87+
void DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
8288
{
83-
try {
84-
CHashVerifier<Stream> verifier(&stream);
85-
// de-serialize file header (network specific magic number) and ..
86-
unsigned char pchMsgTmp[4];
87-
verifier >> pchMsgTmp;
88-
// ... verify the network matches ours
89-
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
90-
return error("%s: Invalid network magic number", __func__);
91-
92-
// de-serialize data
93-
verifier >> data;
94-
95-
// verify checksum
96-
if (fCheckSum) {
97-
uint256 hashTmp;
98-
stream >> hashTmp;
99-
if (hashTmp != verifier.GetHash()) {
100-
return error("%s: Checksum mismatch, data corrupted", __func__);
101-
}
89+
CHashVerifier<Stream> verifier(&stream);
90+
// de-serialize file header (network specific magic number) and ..
91+
unsigned char pchMsgTmp[4];
92+
verifier >> pchMsgTmp;
93+
// ... verify the network matches ours
94+
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) {
95+
throw std::runtime_error{"Invalid network magic number"};
96+
}
97+
98+
// de-serialize data
99+
verifier >> data;
100+
101+
// verify checksum
102+
if (fCheckSum) {
103+
uint256 hashTmp;
104+
stream >> hashTmp;
105+
if (hashTmp != verifier.GetHash()) {
106+
throw std::runtime_error{"Checksum mismatch, data corrupted"};
102107
}
103108
}
104-
catch (const std::exception& e) {
105-
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
106-
}
107-
108-
return true;
109109
}
110110

111111
template <typename Data>
112-
bool DeserializeFileDB(const fs::path& path, Data& data, int version)
112+
void DeserializeFileDB(const fs::path& path, Data& data, int version)
113113
{
114114
// open input file, and associate with CAutoFile
115115
FILE* file = fsbridge::fopen(path, "rb");
116116
CAutoFile filein(file, SER_DISK, version);
117117
if (filein.IsNull()) {
118-
LogPrintf("Missing or invalid file %s\n", path.string());
119-
return false;
118+
throw DbNotFoundError{};
120119
}
121-
return DeserializeDB(filein, data);
120+
DeserializeDB(filein, data);
122121
}
123122
} // namespace
124123

@@ -177,9 +176,9 @@ bool DumpPeerAddresses(const ArgsManager& args, const CAddrMan& addr)
177176
return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
178177
}
179178

180-
bool ReadFromStream(CAddrMan& addr, CDataStream& ssPeers)
179+
void ReadFromStream(CAddrMan& addr, CDataStream& ssPeers)
181180
{
182-
return DeserializeDB(ssPeers, addr, false);
181+
DeserializeDB(ssPeers, addr, false);
183182
}
184183

185184
std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<CAddrMan>& addrman)
@@ -189,13 +188,18 @@ std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const A
189188

190189
int64_t nStart = GetTimeMillis();
191190
const auto path_addr{args.GetDataDirNet() / "peers.dat"};
192-
if (DeserializeFileDB(path_addr, *addrman, CLIENT_VERSION)) {
191+
try {
192+
DeserializeFileDB(path_addr, *addrman, CLIENT_VERSION);
193193
LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(), GetTimeMillis() - nStart);
194-
} else {
194+
} catch (const DbNotFoundError&) {
195195
// Addrman can be in an inconsistent state after failure, reset it
196196
addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
197-
LogPrintf("Recreating peers.dat\n");
197+
LogPrintf("Creating peers.dat because the file was not found (%s)\n", path_addr);
198198
DumpPeerAddresses(args, *addrman);
199+
} catch (const std::exception& e) {
200+
addrman = nullptr;
201+
return strprintf(_("Invalid or corrupt peers.dat (%s). If you believe this is a bug, please report it to %s. As a workaround, you can move the file (%s) out of the way (rename, move, or delete) to have a new one created on the next start."),
202+
e.what(), PACKAGE_BUGREPORT, path_addr);
199203
}
200204
return std::nullopt;
201205
}
@@ -209,9 +213,10 @@ void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& a
209213
std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
210214
{
211215
std::vector<CAddress> anchors;
212-
if (DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT)) {
216+
try {
217+
DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
213218
LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename());
214-
} else {
219+
} catch (const std::exception&) {
215220
anchors.clear();
216221
}
217222

src/addrdb.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct bilingual_str;
2121

2222
bool DumpPeerAddresses(const ArgsManager& args, const CAddrMan& addr);
2323
/** Only used by tests. */
24-
bool ReadFromStream(CAddrMan& addr, CDataStream& ssPeers);
24+
void ReadFromStream(CAddrMan& addr, CDataStream& ssPeers);
2525

2626
/** Access to the banlist database (banlist.json) */
2727
class CBanDB

src/test/addrman_tests.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,7 @@ BOOST_AUTO_TEST_CASE(load_addrman)
10431043

10441044
CAddrMan addrman2(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
10451045
BOOST_CHECK(addrman2.size() == 0);
1046-
BOOST_CHECK(ReadFromStream(addrman2, ssPeers2));
1046+
ReadFromStream(addrman2, ssPeers2);
10471047
BOOST_CHECK(addrman2.size() == 3);
10481048
}
10491049

@@ -1073,7 +1073,7 @@ BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
10731073

10741074
CAddrMan addrman2(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
10751075
BOOST_CHECK(addrman2.size() == 0);
1076-
BOOST_CHECK(!ReadFromStream(addrman2, ssPeers2));
1076+
BOOST_CHECK_THROW(ReadFromStream(addrman2, ssPeers2), std::ios_base::failure);
10771077
}
10781078

10791079

src/test/fuzz/data_stream.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,8 @@ FUZZ_TARGET_INIT(data_stream_addr_man, initialize_data_stream_addr_man)
2323
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
2424
CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
2525
CAddrMan addr_man(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
26-
ReadFromStream(addr_man, data_stream);
26+
try {
27+
ReadFromStream(addr_man, data_stream);
28+
} catch (const std::exception&) {
29+
}
2730
}

test/functional/feature_addrman.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from test_framework.messages import ser_uint256, hash256
1111
from test_framework.p2p import MAGIC_BYTES
1212
from test_framework.test_framework import BitcoinTestFramework
13+
from test_framework.test_node import ErrorMatch
1314
from test_framework.util import assert_equal
1415

1516

@@ -43,6 +44,12 @@ def set_test_params(self):
4344

4445
def run_test(self):
4546
peers_dat = os.path.join(self.nodes[0].datadir, self.chain, "peers.dat")
47+
init_error = lambda reason: (
48+
f"Error: Invalid or corrupt peers.dat \\({reason}\\). If you believe this "
49+
f"is a bug, please report it to {self.config['environment']['PACKAGE_BUGREPORT']}. "
50+
f'As a workaround, you can move the file \\("{peers_dat}"\\) out of the way \\(rename, '
51+
"move, or delete\\) to have a new one created on the next start."
52+
)
4653

4754
self.log.info("Check that mocked addrman is valid")
4855
self.stop_node(0)
@@ -54,30 +61,29 @@ def run_test(self):
5461
self.log.info("Check that addrman from future cannot be read")
5562
self.stop_node(0)
5663
write_addrman(peers_dat, lowest_compatible=111)
57-
with self.nodes[0].assert_debug_log([
58-
f'ERROR: DeserializeDB: Deserialize or I/O error - Unsupported format of addrman database: 1. It is compatible with formats >=111, but the maximum supported by this version of {self.config["environment"]["PACKAGE_NAME"]} is 3.',
59-
"Recreating peers.dat",
60-
]):
61-
self.start_node(0)
62-
assert_equal(self.nodes[0].getnodeaddresses(), [])
64+
self.nodes[0].assert_start_raises_init_error(
65+
expected_msg=init_error(
66+
"Unsupported format of addrman database: 1. It is compatible with "
67+
"formats >=111, but the maximum supported by this version of "
68+
f"{self.config['environment']['PACKAGE_NAME']} is 3.: (.+)"
69+
),
70+
match=ErrorMatch.FULL_REGEX,
71+
)
6372

6473
self.log.info("Check that corrupt addrman cannot be read")
6574
self.stop_node(0)
6675
with open(peers_dat, "wb") as f:
6776
f.write(serialize_addrman()[:-1])
68-
with self.nodes[0].assert_debug_log([
69-
"ERROR: DeserializeDB: Deserialize or I/O error - CAutoFile::read: end of file",
70-
"Recreating peers.dat",
71-
]):
72-
self.start_node(0)
73-
assert_equal(self.nodes[0].getnodeaddresses(), [])
77+
self.nodes[0].assert_start_raises_init_error(
78+
expected_msg=init_error("CAutoFile::read: end of file.*"),
79+
match=ErrorMatch.FULL_REGEX,
80+
)
7481

7582
self.log.info("Check that missing addrman is recreated")
7683
self.stop_node(0)
7784
os.remove(peers_dat)
7885
with self.nodes[0].assert_debug_log([
79-
f"Missing or invalid file {peers_dat}",
80-
"Recreating peers.dat",
86+
f'Creating peers.dat because the file was not found ("{peers_dat}")',
8187
]):
8288
self.start_node(0)
8389
assert_equal(self.nodes[0].getnodeaddresses(), [])

0 commit comments

Comments
 (0)