Skip to content

Commit 89df7f2

Browse files
vasildfurszy
authored andcommitted
addrman: ensure old versions don't parse peers.dat
Even though the format of `peers.dat` was changed in an incompatible way (old software versions <0.21 cannot understand the new file format), it is not guaranteed that old versions will fail to parse it. There is a chance that old versions parse its contents as garbage and use it. Old versions expect the "key size" field to be 32 and fail the parsing if it is not. Thus, we put something other than 32 in it. This will make versions between 0.11.0 and 0.20.1 deterministically fail on the new format. Versions prior to bitcoin#5941 (<0.11.0) will still parse it as garbage. Also, introduce a way to increment the `peers.dat` format in a way that does not necessary make older versions refuse to read it.
1 parent bb90c5c commit 89df7f2

File tree

2 files changed

+58
-27
lines changed

2 files changed

+58
-27
lines changed

doc/release-notes.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ format of this file has been changed in a backwards-incompatible way in order to
4343
accommodate the storage of Tor v3 and other BIP155 addresses. This means that if
4444
the file is modified by v5.3 or newer then older versions will not be able to
4545
read it. Those old versions, in the event of a downgrade, will log an error
46-
message that deserialization has failed and will continue normal operation
47-
as if the file was missing, creating a new empty one. (#2411)
46+
message "Incorrect keysize in addrman deserialization" and will continue normal
47+
operation as if the file was missing, creating a new empty one. (#2411)
4848

4949
Notable Changes
5050
==============

src/addrman.h

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
#ifndef BITCOIN_ADDRMAN_H
88
#define BITCOIN_ADDRMAN_H
99

10+
#if defined(HAVE_CONFIG_H)
11+
#include "config/pivx-config.h"
12+
#endif //HAVE_CONFIG_H
13+
1014
#include "clientversion.h"
1115
#include "netaddress.h"
1216
#include "protocol.h"
@@ -183,6 +187,28 @@ friend class CAddrManTest;
183187
mutable RecursiveMutex cs;
184188

185189
private:
190+
//! Serialization versions.
191+
enum Format : uint8_t {
192+
V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
193+
V1_DETERMINISTIC = 1, //!< for pre-asmap files
194+
V2_ASMAP = 2, //!< for files including asmap version
195+
V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format
196+
};
197+
198+
//! The maximum format this software knows it can unserialize. Also, we always serialize
199+
//! in this format.
200+
//! The format (first byte in the serialized stream) can be higher than this and
201+
//! still this software may be able to unserialize the file - if the second byte
202+
//! (see `lowest_compatible` in `Unserialize()`) is less or equal to this.
203+
static constexpr Format FILE_FORMAT = Format::V3_BIP155;
204+
205+
//! The initial value of a field that is incremented every time an incompatible format
206+
//! change is made (such that old software versions would not be able to parse and
207+
//! understand the new file format). This is 32 because we overtook the "key size"
208+
//! field which was 32 historically.
209+
//! @note Don't increment this. Increment `lowest_compatible` in `Serialize()` instead.
210+
static constexpr uint8_t INCOMPATIBILITY_BASE = 32;
211+
186212
//! last used nId
187213
int nIdCount GUARDED_BY(cs);
188214

@@ -272,14 +298,6 @@ friend class CAddrManTest;
272298
void SetServices_(const CService& addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
273299

274300
public:
275-
//! Serialization versions.
276-
enum class Format : uint8_t {
277-
V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
278-
V1_DETERMINISTIC = 1, //!< for pre-asmap files
279-
V2_ASMAP = 2, //!< for files including asmap version
280-
V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format
281-
};
282-
283301
// Compressed IP->ASN mapping, loaded from a file when a node starts.
284302
// Should be always empty if no file was provided.
285303
// This mapping is then used for bucketing nodes in Addrman.
@@ -302,8 +320,18 @@ friend class CAddrManTest;
302320

303321
/**
304322
* Serialized format.
305-
* * version byte (@see `Format`)
306-
* * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
323+
* * format version byte (@see `Format`)
324+
* * lowest compatible format version byte. This is used to help old software decide
325+
* whether to parse the file. For example:
326+
* * PIVX Core version N knows how to parse up to format=3. If a new format=4 is
327+
* introduced in version N+1 that is compatible with format=3 and it is known that
328+
* version N will be able to parse it, then version N+1 will write
329+
* (format=4, lowest_compatible=3) in the first two bytes of the file, and so
330+
* version N will still try to parse it.
331+
* * PIVX Core version N+2 introduces a new incompatible format=5. It will write
332+
* (format=5, lowest_compatible=5) and so any versions that do not know how to parse
333+
* format=5 will not try to read the file.
334+
* * nKey
307335
* * nNew
308336
* * nTried
309337
* * number of "new" buckets XOR 2**30
@@ -334,12 +362,17 @@ friend class CAddrManTest;
334362
{
335363
LOCK(cs);
336364

337-
// Always serialize in the latest version (currently Format::V3_BIP155).
365+
// Always serialize in the latest version (FILE_FORMAT).
338366

339367
OverrideStream<Stream> s(&s_, s_.GetType(), s_.GetVersion() | ADDRV2_FORMAT);
340368

341-
s << static_cast<uint8_t>(Format::V3_BIP155);
342-
s << ((unsigned char)32);
369+
s << static_cast<uint8_t>(FILE_FORMAT);
370+
371+
// Increment `lowest_compatible` if a newly introduced format is incompatible with
372+
// the previous one.
373+
static constexpr uint8_t lowest_compatible = Format::V3_BIP155;
374+
s << static_cast<uint8_t>(INCOMPATIBILITY_BASE + lowest_compatible);
375+
343376
s << nKey;
344377
s << nNew;
345378
s << nTried;
@@ -399,15 +432,6 @@ friend class CAddrManTest;
399432
Format format;
400433
s_ >> Using<CustomUintFormatter<1>>(format);
401434

402-
static constexpr Format maximum_supported_format = Format::V3_BIP155;
403-
if (format > maximum_supported_format) {
404-
throw std::ios_base::failure(strprintf(
405-
"Unsupported format of addrman database: %u. Maximum supported is %u. "
406-
"Continuing operation without using the saved list of peers.",
407-
static_cast<uint8_t>(format),
408-
static_cast<uint8_t>(maximum_supported_format)));
409-
}
410-
411435
int stream_version = s_.GetVersion();
412436
if (format >= Format::V3_BIP155) {
413437
// Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
@@ -417,9 +441,16 @@ friend class CAddrManTest;
417441

418442
OverrideStream<Stream> s(&s_, s_.GetType(), stream_version);
419443

420-
unsigned char nKeySize;
421-
s >> nKeySize;
422-
if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
444+
uint8_t compat;
445+
s >> compat;
446+
const uint8_t lowest_compatible = compat - INCOMPATIBILITY_BASE;
447+
if (lowest_compatible > FILE_FORMAT) {
448+
throw std::ios_base::failure(strprintf(
449+
"Unsupported format of addrman database: %u. It is compatible with formats >=%u, "
450+
"but the maximum supported by this version of %s is %u.",
451+
format, lowest_compatible, PACKAGE_NAME, static_cast<uint8_t>(FILE_FORMAT)));
452+
}
453+
423454
s >> nKey;
424455
s >> nNew;
425456
s >> nTried;

0 commit comments

Comments
 (0)