diff options
author | Russell Yanofsky <russ@yanofsky.org> | 2017-10-10 15:27:26 -0400 |
---|---|---|
committer | MarcoFalke <falke.marco@gmail.com> | 2017-11-01 15:23:06 -0400 |
commit | 9c8006dc332139069d798f39406ec67186ecb311 (patch) | |
tree | 84b4f3330847e5bb0d1a5ddcb1827123a0f6caec /src/wallet/db.cpp | |
parent | de7053f1141756158c2107bcba376401b4cc94e9 (diff) | |
download | bitcoin-9c8006dc332139069d798f39406ec67186ecb311.tar.xz |
Avoid opening copied wallet databases simultaneously
Make sure wallet databases have unique fileids. If they don't, throw an error.
BDB caches do not work properly when more than one open database has the same
fileid, because values written to one database may show up in reads to other
databases.
Bitcoin will never create different databases with the same fileid, but users
can create them by manually copying database files.
BDB caching bug was reported by Chris Moore <dooglus@gmail.com>
https://github.com/bitcoin/bitcoin/issues/11429
Fixes #11429
Github-Pull: #11476
Rebased-From: 478a89c1ef79a75275d1b508122c06eee9386b2d
Diffstat (limited to 'src/wallet/db.cpp')
-rw-r--r-- | src/wallet/db.cpp | 35 |
1 files changed, 35 insertions, 0 deletions
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index f5f96768b0..fb6e576074 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -20,6 +20,40 @@ #include <boost/thread.hpp> +namespace { +//! Make sure database has a unique fileid within the environment. If it +//! doesn't, throw an error. BDB caches do not work properly when more than one +//! open database has the same fileid (values written to one database may show +//! up in reads to other databases). +//! +//! BerkeleyDB generates unique fileids by default +//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html), +//! so bitcoin should never create different databases with the same fileid, but +//! this error can be triggered if users manually copy database files. +void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db) +{ + if (env.IsMock()) return; + + u_int8_t fileid[DB_FILE_ID_LEN]; + int ret = db.get_mpf()->get_fileid(fileid); + if (ret != 0) { + throw std::runtime_error(strprintf("CDB: Can't open database %s (get_fileid failed with %d)", filename, ret)); + } + + for (const auto& item : env.mapDb) { + u_int8_t item_fileid[DB_FILE_ID_LEN]; + if (item.second && item.second->get_mpf()->get_fileid(item_fileid) == 0 && + memcmp(fileid, item_fileid, sizeof(fileid)) == 0) { + const char* item_filename = nullptr; + item.second->get_dbname(&item_filename, nullptr); + throw std::runtime_error(strprintf("CDB: Can't open database %s (duplicates fileid %s from %s)", filename, + HexStr(std::begin(item_fileid), std::end(item_fileid)), + item_filename ? item_filename : "(unknown database)")); + } + } +} +} // namespace + // // CDB // @@ -403,6 +437,7 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb if (ret != 0) { throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename)); } + CheckUniqueFileid(*env, strFilename, *pdb_temp); pdb = pdb_temp.release(); env->mapDb[strFilename] = pdb; |