forked from bitcoin/bitcoin
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix de-serialization bug where AddrMan is corrupted after exception
* CAddrDB modified so that when de-serialization code throws an exception Addrman is reset to a clean state * CAddrDB modified to make unit tests possible * Regression test created to ensure bug is fixed * StartNode modifed to clear adrman if CAddrDB::Read returns an error code.
- Loading branch information
1 parent
18bd337
commit d7a3e78
Showing
4 changed files
with
150 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
// Copyright (c) 2012-2016 The Bitcoin Core developers | ||
// Distributed under the MIT software license, see the accompanying | ||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
#include "addrman.h" | ||
#include "test/test_bitcoin.h" | ||
#include <string> | ||
#include <boost/test/unit_test.hpp> | ||
#include "hash.h" | ||
#include "serialize.h" | ||
#include "streams.h" | ||
#include "net.h" | ||
#include "chainparams.h" | ||
|
||
using namespace std; | ||
|
||
class CAddrManSerializationMock : public CAddrMan | ||
{ | ||
public: | ||
virtual void Serialize(CDataStream& s, int nType, int nVersionDummy) const = 0; | ||
}; | ||
|
||
class CAddrManUncorrupted : public CAddrManSerializationMock | ||
{ | ||
public: | ||
void Serialize(CDataStream& s, int nType, int nVersionDummy) const | ||
{ | ||
CAddrMan::Serialize(s, nType, nVersionDummy); | ||
} | ||
}; | ||
|
||
class CAddrManCorrupted : public CAddrManSerializationMock | ||
{ | ||
public: | ||
void Serialize(CDataStream& s, int nType, int nVersionDummy) const | ||
{ | ||
// Produces corrupt output that claims addrman has 20 addrs when it only has one addr. | ||
unsigned char nVersion = 1; | ||
s << nVersion; | ||
s << ((unsigned char)32); | ||
s << nKey; | ||
s << 10; // nNew | ||
s << 10; // nTried | ||
|
||
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30); | ||
s << nUBuckets; | ||
|
||
CAddress addr = CAddress(CService("252.1.1.1", 7777)); | ||
CAddrInfo info = CAddrInfo(addr, CNetAddr("252.2.2.2")); | ||
s << info; | ||
} | ||
}; | ||
|
||
CDataStream AddrmanToStream(CAddrManSerializationMock& addrman) | ||
{ | ||
CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION); | ||
ssPeersIn << FLATDATA(Params().MessageStart()); | ||
ssPeersIn << addrman; | ||
std::string str = ssPeersIn.str(); | ||
vector<unsigned char> vchData(str.begin(), str.end()); | ||
return CDataStream(vchData, SER_DISK, CLIENT_VERSION); | ||
} | ||
|
||
BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup) | ||
|
||
BOOST_AUTO_TEST_CASE(caddrdb_read) | ||
{ | ||
CAddrManUncorrupted addrmanUncorrupted; | ||
|
||
CService addr1 = CService("250.7.1.1", 8333); | ||
CService addr2 = CService("250.7.2.2", 9999); | ||
CService addr3 = CService("250.7.3.3", 9999); | ||
|
||
// Add three addresses to new table. | ||
addrmanUncorrupted.Add(CAddress(addr1), CService("252.5.1.1", 8333)); | ||
addrmanUncorrupted.Add(CAddress(addr2), CService("252.5.1.1", 8333)); | ||
addrmanUncorrupted.Add(CAddress(addr3), CService("252.5.1.1", 8333)); | ||
|
||
// Test that the de-serialization does not throw an exception. | ||
CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted); | ||
bool exceptionThrown = false; | ||
CAddrMan addrman1; | ||
|
||
BOOST_CHECK(addrman1.size() == 0); | ||
try { | ||
unsigned char pchMsgTmp[4]; | ||
ssPeers1 >> FLATDATA(pchMsgTmp); | ||
ssPeers1 >> addrman1; | ||
} catch (const std::exception& e) { | ||
exceptionThrown = true; | ||
} | ||
|
||
BOOST_CHECK(addrman1.size() == 3); | ||
BOOST_CHECK(exceptionThrown == false); | ||
|
||
// Test that CAddrDB::Read creates an addrman with the correct number of addrs. | ||
CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted); | ||
|
||
CAddrMan addrman2; | ||
CAddrDB adb; | ||
BOOST_CHECK(addrman2.size() == 0); | ||
adb.Read(addrman2, ssPeers2); | ||
BOOST_CHECK(addrman2.size() == 3); | ||
} | ||
|
||
|
||
BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted) | ||
{ | ||
CAddrManCorrupted addrmanCorrupted; | ||
|
||
// Test that the de-serialization of corrupted addrman throws an exception. | ||
CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted); | ||
bool exceptionThrown = false; | ||
CAddrMan addrman1; | ||
BOOST_CHECK(addrman1.size() == 0); | ||
try { | ||
unsigned char pchMsgTmp[4]; | ||
ssPeers1 >> FLATDATA(pchMsgTmp); | ||
ssPeers1 >> addrman1; | ||
} catch (const std::exception& e) { | ||
exceptionThrown = true; | ||
} | ||
// Even through de-serialization failed adddrman is not left in a clean state. | ||
BOOST_CHECK(addrman1.size() == 1); | ||
BOOST_CHECK(exceptionThrown); | ||
|
||
// Test that CAddrDB::Read leaves addrman in a clean state if de-serialization fails. | ||
CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted); | ||
|
||
CAddrMan addrman2; | ||
CAddrDB adb; | ||
BOOST_CHECK(addrman2.size() == 0); | ||
adb.Read(addrman2, ssPeers2); | ||
BOOST_CHECK(addrman2.size() == 0); | ||
} | ||
|
||
BOOST_AUTO_TEST_SUITE_END() |