aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormerge-script <falke.marco@gmail.com>2021-09-10 11:41:20 +0200
committermerge-script <falke.marco@gmail.com>2021-09-10 11:41:20 +0200
commit053a5fc7d912d597cd6dc7376b479420d1eae1c0 (patch)
tree175be476490ffe5608a67cf108edd801f20d8e6b /src
parent384d07601185d0779c324392e798da234163ca72 (diff)
parentfa55c3dc1b4bbdc6a53bd11fa6c0b2ec6bbb64ae (diff)
downloadbitcoin-053a5fc7d912d597cd6dc7376b479420d1eae1c0.tar.xz
Merge bitcoin/bitcoin#22762: Raise InitError when peers.dat is invalid or corrupted
fa55c3dc1b4bbdc6a53bd11fa6c0b2ec6bbb64ae Raise InitError when peers.dat is invalid or corrupted (MarcoFalke) fa4e2ccfd8ae96c381947285bef47cb39474ac89 Inline ReadPeerAddresses (MarcoFalke) fa5aeec80c6cdca9ca027d80dff3b397911ff2c2 Move LoadAddrman from init to addrdb (MarcoFalke) Pull request description: peers.dat is silently erased when it can not be parsed or when it appears corrupted. Fix that by notifying the user. This might help in the following examples: * The user provided the database, but picked the wrong one. * A future version of Bitcoin Core wrote the file and it can't be read. * The file was corrupted by a logic bug in Bitcoin Core. * The file was corrupted by a disk failure. ACKs for top commit: jonatack: Code review re-ACK fa55c3dc1b4bbdc6a53bd11fa6c0b2ec6bbb64ae per `git range-diff eb1f570 fa59c6d fa55c3` and verified the new tests fail on master, except "Check mocked addrman is valid", as expected prayank23: tACK https://github.com/bitcoin/bitcoin/commit/fa55c3dc1b4bbdc6a53bd11fa6c0b2ec6bbb64ae vasild: ACK fa55c3dc1b4bbdc6a53bd11fa6c0b2ec6bbb64ae Tree-SHA512: 78264a78ee570a3c3262cf9c8542b5ffaffa5f52da1eef66c8c381f346989272967cfe1769c573502d9d7d3f7ad68c3ac3b2ec734185d2e4e7595b7122b14196
Diffstat (limited to 'src')
-rw-r--r--src/addrdb.cpp90
-rw-r--r--src/addrdb.h8
-rw-r--r--src/init.cpp14
-rw-r--r--src/test/addrman_tests.cpp4
-rw-r--r--src/test/fuzz/data_stream.cpp5
5 files changed, 68 insertions, 53 deletions
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 856f318961..1e73750ce3 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -18,8 +18,15 @@
#include <univalue.h>
#include <util/settings.h>
#include <util/system.h>
+#include <util/translation.h>
namespace {
+
+class DbNotFoundError : public std::exception
+{
+ using std::exception::exception;
+};
+
template <typename Stream, typename Data>
bool SerializeDB(Stream& stream, const Data& data)
{
@@ -77,47 +84,40 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
}
template <typename Stream, typename Data>
-bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
+void DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
{
- try {
- CHashVerifier<Stream> verifier(&stream);
- // de-serialize file header (network specific magic number) and ..
- unsigned char pchMsgTmp[4];
- verifier >> pchMsgTmp;
- // ... verify the network matches ours
- if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
- return error("%s: Invalid network magic number", __func__);
-
- // de-serialize data
- verifier >> data;
-
- // verify checksum
- if (fCheckSum) {
- uint256 hashTmp;
- stream >> hashTmp;
- if (hashTmp != verifier.GetHash()) {
- return error("%s: Checksum mismatch, data corrupted", __func__);
- }
- }
- }
- catch (const std::exception& e) {
- return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ CHashVerifier<Stream> verifier(&stream);
+ // de-serialize file header (network specific magic number) and ..
+ unsigned char pchMsgTmp[4];
+ verifier >> pchMsgTmp;
+ // ... verify the network matches ours
+ if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) {
+ throw std::runtime_error{"Invalid network magic number"};
}
- return true;
+ // de-serialize data
+ verifier >> data;
+
+ // verify checksum
+ if (fCheckSum) {
+ uint256 hashTmp;
+ stream >> hashTmp;
+ if (hashTmp != verifier.GetHash()) {
+ throw std::runtime_error{"Checksum mismatch, data corrupted"};
+ }
+ }
}
template <typename Data>
-bool DeserializeFileDB(const fs::path& path, Data& data, int version)
+void DeserializeFileDB(const fs::path& path, Data& data, int version)
{
// open input file, and associate with CAutoFile
FILE* file = fsbridge::fopen(path, "rb");
CAutoFile filein(file, SER_DISK, version);
if (filein.IsNull()) {
- LogPrintf("Missing or invalid file %s\n", path.string());
- return false;
+ throw DbNotFoundError{};
}
- return DeserializeDB(filein, data);
+ DeserializeDB(filein, data);
}
} // namespace
@@ -176,15 +176,32 @@ bool DumpPeerAddresses(const ArgsManager& args, const CAddrMan& addr)
return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
}
-bool ReadPeerAddresses(const ArgsManager& args, CAddrMan& addr)
+void ReadFromStream(CAddrMan& addr, CDataStream& ssPeers)
{
- const auto pathAddr = args.GetDataDirNet() / "peers.dat";
- return DeserializeFileDB(pathAddr, addr, CLIENT_VERSION);
+ DeserializeDB(ssPeers, addr, false);
}
-bool ReadFromStream(CAddrMan& addr, CDataStream& ssPeers)
+std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<CAddrMan>& addrman)
{
- return DeserializeDB(ssPeers, addr, false);
+ auto check_addrman = std::clamp<int32_t>(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
+ addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
+
+ int64_t nStart = GetTimeMillis();
+ const auto path_addr{args.GetDataDirNet() / "peers.dat"};
+ try {
+ DeserializeFileDB(path_addr, *addrman, CLIENT_VERSION);
+ LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(), GetTimeMillis() - nStart);
+ } catch (const DbNotFoundError&) {
+ // Addrman can be in an inconsistent state after failure, reset it
+ addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
+ LogPrintf("Creating peers.dat because the file was not found (%s)\n", path_addr);
+ DumpPeerAddresses(args, *addrman);
+ } catch (const std::exception& e) {
+ addrman = nullptr;
+ 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."),
+ e.what(), PACKAGE_BUGREPORT, path_addr);
+ }
+ return std::nullopt;
}
void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
@@ -196,9 +213,10 @@ void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& a
std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
{
std::vector<CAddress> anchors;
- if (DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT)) {
+ try {
+ DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename());
- } else {
+ } catch (const std::exception&) {
anchors.clear();
}
diff --git a/src/addrdb.h b/src/addrdb.h
index c31c126ee3..33cc1f9204 100644
--- a/src/addrdb.h
+++ b/src/addrdb.h
@@ -10,17 +10,18 @@
#include <net_types.h> // For banmap_t
#include <univalue.h>
+#include <optional>
#include <vector>
class ArgsManager;
class CAddrMan;
class CAddress;
class CDataStream;
+struct bilingual_str;
bool DumpPeerAddresses(const ArgsManager& args, const CAddrMan& addr);
-bool ReadPeerAddresses(const ArgsManager& args, CAddrMan& addr);
/** Only used by tests. */
-bool ReadFromStream(CAddrMan& addr, CDataStream& ssPeers);
+void ReadFromStream(CAddrMan& addr, CDataStream& ssPeers);
/** Access to the banlist database (banlist.json) */
class CBanDB
@@ -46,6 +47,9 @@ public:
bool Read(banmap_t& banSet);
};
+/** Returns an error string on failure */
+std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<CAddrMan>& addrman);
+
/**
* Dump the anchor IP address database (anchors.dat)
*
diff --git a/src/init.cpp b/src/init.cpp
index 2753745843..d4ba441b0c 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1200,19 +1200,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
LogPrintf("Using /16 prefix for IP bucketing\n");
}
- auto check_addrman = std::clamp<int32_t>(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
- node.addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
-
- // Load addresses from peers.dat
uiInterface.InitMessage(_("Loading P2P addresses…").translated);
- int64_t nStart = GetTimeMillis();
- if (ReadPeerAddresses(args, *node.addrman)) {
- LogPrintf("Loaded %i addresses from peers.dat %dms\n", node.addrman->size(), GetTimeMillis() - nStart);
- } else {
- // Addrman can be in an inconsistent state after failure, reset it
- node.addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
- LogPrintf("Recreating peers.dat\n");
- DumpPeerAddresses(args, *node.addrman);
+ if (const auto error{LoadAddrman(asmap, args, node.addrman)}) {
+ return InitError(*error);
}
}
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 835b18d42e..01a492a20b 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -1043,7 +1043,7 @@ BOOST_AUTO_TEST_CASE(load_addrman)
CAddrMan addrman2(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
BOOST_CHECK(addrman2.size() == 0);
- BOOST_CHECK(ReadFromStream(addrman2, ssPeers2));
+ ReadFromStream(addrman2, ssPeers2);
BOOST_CHECK(addrman2.size() == 3);
}
@@ -1073,7 +1073,7 @@ BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
CAddrMan addrman2(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
BOOST_CHECK(addrman2.size() == 0);
- BOOST_CHECK(!ReadFromStream(addrman2, ssPeers2));
+ BOOST_CHECK_THROW(ReadFromStream(addrman2, ssPeers2), std::ios_base::failure);
}
diff --git a/src/test/fuzz/data_stream.cpp b/src/test/fuzz/data_stream.cpp
index 08e9c91ba3..323090e041 100644
--- a/src/test/fuzz/data_stream.cpp
+++ b/src/test/fuzz/data_stream.cpp
@@ -23,5 +23,8 @@ FUZZ_TARGET_INIT(data_stream_addr_man, initialize_data_stream_addr_man)
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
CAddrMan addr_man(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
- ReadFromStream(addr_man, data_stream);
+ try {
+ ReadFromStream(addr_man, data_stream);
+ } catch (const std::exception&) {
+ }
}