Browse Source

Merge pull request #205 from KomodoPlatform/fix_PING

fix PING/REJECT attack [CVE-2019-17048, CVE-2019-16930]
pull/37/head
ca333 5 years ago
committed by GitHub
parent
commit
6b79766999
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      qa/rpc-tests/test_framework/util.py
  2. 22
      src/init.cpp
  3. 2
      src/main.cpp
  4. 4
      src/rpc/blockchain.cpp
  5. 47
      src/txmempool.cpp
  6. 7
      src/txmempool.h
  7. 16
      src/wallet/wallet.cpp
  8. 2
      src/wallet/wallet.h
  9. 50
      src/zcash/Note.cpp

10
qa/rpc-tests/test_framework/util.py

@ -56,7 +56,7 @@ def sync_blocks(rpc_connections, wait=1):
def sync_mempools(rpc_connections, wait=1): def sync_mempools(rpc_connections, wait=1):
""" """
Wait until everybody has the same transactions in their memory Wait until everybody has the same transactions in their memory
pools pools, and has notified all internal listeners of them
""" """
while True: while True:
pool = set(rpc_connections[0].getrawmempool()) pool = set(rpc_connections[0].getrawmempool())
@ -68,6 +68,14 @@ def sync_mempools(rpc_connections, wait=1):
break break
time.sleep(wait) time.sleep(wait)
# Now that the mempools are in sync, wait for the internal
# notifications to finish
while True:
notified = [ x.getmempoolinfo()['fullyNotified'] for x in rpc_connections ]
if notified == [ True ] * len(notified):
break
time.sleep(wait)
bitcoind_processes = {} bitcoind_processes = {}
def initialize_datadir(dirname, n): def initialize_datadir(dirname, n):

22
src/init.cpp

@ -74,7 +74,9 @@
#include <boost/function.hpp> #include <boost/function.hpp>
#include <boost/interprocess/sync/file_lock.hpp> #include <boost/interprocess/sync/file_lock.hpp>
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <chrono>
#include <openssl/crypto.h> #include <openssl/crypto.h>
#include <thread>
#include <libsnark/common/profiling.hpp> #include <libsnark/common/profiling.hpp>
@ -727,6 +729,22 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
} }
} }
void ThreadNotifyRecentlyAdded()
{
while (true) {
// Run the notifier on an integer second in the steady clock.
auto now = std::chrono::steady_clock::now().time_since_epoch();
auto nextFire = std::chrono::duration_cast<std::chrono::seconds>(
now + std::chrono::seconds(1));
std::this_thread::sleep_until(
std::chrono::time_point<std::chrono::steady_clock>(nextFire));
boost::this_thread::interruption_point();
mempool.NotifyRecentlyAdded();
}
}
/** Sanity checks /** Sanity checks
* Ensure that Bitcoin is running in a usable environment with all * Ensure that Bitcoin is running in a usable environment with all
* necessary library support. * necessary library support.
@ -1964,6 +1982,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0); LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0);
#endif #endif
// Start the thread that notifies listeners of transactions that have been
// recently added to the mempool.
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "txnotify", &ThreadNotifyRecentlyAdded));
if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
StartTorControl(threadGroup, scheduler); StartTorControl(threadGroup, scheduler);

2
src/main.cpp

@ -2080,8 +2080,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
} }
} }
SyncWithWallets(tx, NULL);
return true; return true;
} }

4
src/rpc/blockchain.cpp

@ -1906,6 +1906,10 @@ UniValue mempoolInfoToJSON()
ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize())); ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize()));
ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage())); ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage()));
if (Params().NetworkIDString() == "regtest") {
ret.push_back(Pair("fullyNotified", mempool.IsFullyNotified()));
}
return ret; return ret;
} }

47
src/txmempool.cpp

@ -29,6 +29,7 @@
#include "timedata.h" #include "timedata.h"
#include "util.h" #include "util.h"
#include "utilmoneystr.h" #include "utilmoneystr.h"
#include "validationinterface.h"
#include "version.h" #include "version.h"
#define _COINBASE_MATURITY 100 #define _COINBASE_MATURITY 100
@ -119,6 +120,8 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
LOCK(cs); LOCK(cs);
mapTx.insert(entry); mapTx.insert(entry);
const CTransaction& tx = mapTx.find(hash)->GetTx(); const CTransaction& tx = mapTx.find(hash)->GetTx();
mapRecentlyAddedTx[tx.GetHash()] = &tx;
nRecentlyAddedSequence += 1;
if (!tx.IsCoinImport()) { if (!tx.IsCoinImport()) {
for (unsigned int i = 0; i < tx.vin.size(); i++) for (unsigned int i = 0; i < tx.vin.size(); i++)
{ {
@ -365,6 +368,7 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem
txToRemove.push_back(it->second.ptx->GetHash()); txToRemove.push_back(it->second.ptx->GetHash());
} }
} }
mapRecentlyAddedTx.erase(hash);
BOOST_FOREACH(const CTxIn& txin, tx.vin) BOOST_FOREACH(const CTxIn& txin, tx.vin)
mapNextTx.erase(txin.prevout); mapNextTx.erase(txin.prevout);
BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) { BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) {
@ -838,6 +842,49 @@ bool CTxMemPool::nullifierExists(const uint256& nullifier, ShieldedType type) co
} }
} }
void CTxMemPool::NotifyRecentlyAdded()
{
uint64_t recentlyAddedSequence;
std::vector<CTransaction> txs;
{
LOCK(cs);
recentlyAddedSequence = nRecentlyAddedSequence;
for (const auto& kv : mapRecentlyAddedTx) {
txs.push_back(*(kv.second));
}
mapRecentlyAddedTx.clear();
}
// A race condition can occur here between these SyncWithWallets calls, and
// the ones triggered by block logic (in ConnectTip and DisconnectTip). It
// is harmless because calling SyncWithWallets(_, NULL) does not alter the
// wallet transaction's block information.
for (auto tx : txs) {
try {
SyncWithWallets(tx, NULL);
} catch (const boost::thread_interrupted&) {
throw;
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "CTxMemPool::NotifyRecentlyAdded()");
} catch (...) {
PrintExceptionContinue(NULL, "CTxMemPool::NotifyRecentlyAdded()");
}
}
// Update the notified sequence number. We only need this in regtest mode,
// and should not lock on cs after calling SyncWithWallets otherwise.
if (Params().NetworkIDString() == "regtest") {
LOCK(cs);
nNotifiedSequence = recentlyAddedSequence;
}
}
bool CTxMemPool::IsFullyNotified() {
assert(Params().NetworkIDString() == "regtest");
LOCK(cs);
return nRecentlyAddedSequence == nNotifiedSequence;
}
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
bool CCoinsViewMemPool::GetNullifier(const uint256 &nf, ShieldedType type) const bool CCoinsViewMemPool::GetNullifier(const uint256 &nf, ShieldedType type) const

7
src/txmempool.h

@ -148,6 +148,10 @@ private:
uint64_t totalTxSize = 0; //! sum of all mempool tx' byte sizes uint64_t totalTxSize = 0; //! sum of all mempool tx' byte sizes
uint64_t cachedInnerUsage; //! sum of dynamic memory usage of all the map elements (NOT the maps themselves) uint64_t cachedInnerUsage; //! sum of dynamic memory usage of all the map elements (NOT the maps themselves)
std::map<uint256, const CTransaction*> mapRecentlyAddedTx;
uint64_t nRecentlyAddedSequence = 0;
uint64_t nNotifiedSequence = 0;
std::map<uint256, const CTransaction*> mapSproutNullifiers; std::map<uint256, const CTransaction*> mapSproutNullifiers;
std::map<uint256, const CTransaction*> mapSaplingNullifiers; std::map<uint256, const CTransaction*> mapSaplingNullifiers;
@ -234,6 +238,9 @@ public:
bool nullifierExists(const uint256& nullifier, ShieldedType type) const; bool nullifierExists(const uint256& nullifier, ShieldedType type) const;
void NotifyRecentlyAdded();
bool IsFullyNotified();
unsigned long size() unsigned long size()
{ {
LOCK(cs); LOCK(cs);

16
src/wallet/wallet.cpp

@ -1841,7 +1841,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock) void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock)
{ {
LOCK2(cs_main, cs_wallet); LOCK(cs_wallet);
if (!AddToWalletIfInvolvingMe(tx, pblock, true)) if (!AddToWalletIfInvolvingMe(tx, pblock, true))
return; // Not one of ours return; // Not one of ours
@ -4830,9 +4830,8 @@ CWalletKey::CWalletKey(int64_t nExpires)
nTimeExpires = nExpires; nTimeExpires = nExpires;
} }
int CMerkleTx::SetMerkleBranch(const CBlock& block) void CMerkleTx::SetMerkleBranch(const CBlock& block)
{ {
AssertLockHeld(cs_main);
CBlock blockTmp; CBlock blockTmp;
// Update the tx's hashBlock // Update the tx's hashBlock
@ -4847,21 +4846,10 @@ int CMerkleTx::SetMerkleBranch(const CBlock& block)
vMerkleBranch.clear(); vMerkleBranch.clear();
nIndex = -1; nIndex = -1;
LogPrintf("ERROR: SetMerkleBranch(): couldn't find tx in block\n"); LogPrintf("ERROR: SetMerkleBranch(): couldn't find tx in block\n");
return 0;
} }
// Fill in merkle branch // Fill in merkle branch
vMerkleBranch = block.GetMerkleBranch(nIndex); vMerkleBranch = block.GetMerkleBranch(nIndex);
// Is the tx in a block that's in the main chain
BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
if (mi == mapBlockIndex.end())
return 0;
const CBlockIndex* pindex = (*mi).second;
if (!pindex || !chainActive.Contains(pindex))
return 0;
return chainActive.Height() - pindex->GetHeight() + 1;
} }
int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const

2
src/wallet/wallet.h

@ -386,7 +386,7 @@ public:
READWRITE(nIndex); READWRITE(nIndex);
} }
int SetMerkleBranch(const CBlock& block); void SetMerkleBranch(const CBlock& block);
/** /**

50
src/zcash/Note.cpp

@ -173,15 +173,21 @@ boost::optional<SaplingOutgoingPlaintext> SaplingOutgoingPlaintext::decrypt(
} }
// Deserialize from the plaintext // Deserialize from the plaintext
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); try {
ss << pt.get(); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << pt.get();
SaplingOutgoingPlaintext ret; SaplingOutgoingPlaintext ret;
ss >> ret; ss >> ret;
assert(ss.size() == 0); assert(ss.size() == 0);
return ret; return ret;
} catch (const boost::thread_interrupted&) {
throw;
} catch (...) {
return boost::none;
}
} }
boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt( boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt(
@ -197,13 +203,17 @@ boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt(
} }
// Deserialize from the plaintext // Deserialize from the plaintext
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << pt.get();
SaplingNotePlaintext ret; SaplingNotePlaintext ret;
ss >> ret; try {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
assert(ss.size() == 0); ss << pt.get();
ss >> ret;
assert(ss.size() == 0);
} catch (const boost::thread_interrupted&) {
throw;
} catch (...) {
return boost::none;
}
uint256 pk_d; uint256 pk_d;
if (!librustzcash_ivk_to_pkd(ivk.begin(), ret.d.data(), pk_d.begin())) { if (!librustzcash_ivk_to_pkd(ivk.begin(), ret.d.data(), pk_d.begin())) {
@ -243,11 +253,17 @@ boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt(
} }
// Deserialize from the plaintext // Deserialize from the plaintext
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << pt.get();
SaplingNotePlaintext ret; SaplingNotePlaintext ret;
ss >> ret; try {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << pt.get();
ss >> ret;
assert(ss.size() == 0);
} catch (const boost::thread_interrupted&) {
throw;
} catch (...) {
return boost::none;
}
uint256 cmu_expected; uint256 cmu_expected;
if (!librustzcash_sapling_compute_cm( if (!librustzcash_sapling_compute_cm(
@ -265,8 +281,6 @@ boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt(
return boost::none; return boost::none;
} }
assert(ss.size() == 0);
return ret; return ret;
} }

Loading…
Cancel
Save