CVE-2017-20154 (published 2022-12-30T21:15Z)

A vulnerability was found in ghostlander Phoenixcoin. It has been classified as problematic. Affected is the function CTxMemPool::accept of the file src/main.cpp. The manipulation leads to denial of service. Upgrading to version 0.6.6.1-pxc is able to address this issue. The name of the patch is 987dd68f71a7d8276cef3b6c3d578fd4845b5699. It is recommended to upgrade the affected component. The identifier of this vulnerability is VDB-217068.

CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-404
cwe_name: Improper Resource Shutdown or Release
description: The product does not release or incorrectly releases a resource before it is made available for re-use.
extended_description: When a resource is created or allocated, the developer is responsible for properly releasing the resource as well as accounting for all potential paths of expiration or invalidation, such as a set period of time or revocation.
         

Fix information:
repo_url: https://github.com/ghostlander/Phoenixcoin
commit_details: repo_url: https://github.com/ghostlander/Phoenixcoin
author: John Doering
author_date: 2017-07-19 08:10:09+00:00
msg: Implement fLimitFree to CTxMemPool::accept()

Works around a potential DoS vulnerability (see Bitcoin commit
ce99358f4aa4182d6983fde3e33a8fdbe1dfe4c3).
repository: repo_url: https://github.com/ghostlander/Phoenixcoin
repo_name: ghostlander/Phoenixcoin
description: Phoenixcoin (PXC) is a decentralised open source digital currency.
repo_language: C++
owner: ghostlander
file_changes: filename: main.cpp
change_type: ModificationType.MODIFY
diff: @@ -480,9 +480,9 @@ bool CTransaction::CheckTransaction() const
 }
 
 
-bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
-                        bool* pfMissingInputs)
-{
+bool CTxMemPool::accept(CTxDB &txdb, CTransaction &tx, bool fCheckInputs,
+  bool fLimitFree, bool *pfMissingInputs) {
+
     if (pfMissingInputs)
         *pfMissingInputs = false;
 
@@ -566,32 +566,29 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
         unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
 
         // Don't accept it if it can't get into a block
-        if(nFees < tx.GetMinFee(nTxSize, true, GMF_RELAY))
-            return error("CTxMemPool::accept() : not enough fees");
+        if(fLimitFree && (nFees < tx.GetMinFee(nTxSize, true, GMF_RELAY)))
+          return(error("CTxMemPool::accept() : not enough fees"));
 
         // Continuously rate-limit free transactions
         // This mitigates 'penny-flooding' -- sending thousands of free transactions just to
         // be annoying or make other's transactions take longer to confirm.
-        if (nFees < MIN_RELAY_TX_FEE)
-        {
-            static CCriticalSection cs;
+        if(fLimitFree && (nFees < MIN_RELAY_TX_FEE)) {
             static double dFreeCount;
             static int64 nLastTime;
             int64 nNow = GetTime();
 
-            {
-                LOCK(cs);
-                // Use an exponentially decaying ~10-minute window:
-                dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
-                nLastTime = nNow;
-                // -limitfreerelay unit is thousand-bytes-per-minute
-                // At default rate it would take over a month to fill 1GB
-                if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe(tx))
-                    return error("CTxMemPool::accept() : free transaction rejected by rate limiter");
-                if (fDebug)
-                    printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nTxSize);
-                dFreeCount += nTxSize;
-            }
+            LOCK(cs);
+
+            // Use an exponentially decaying ~10-minute window:
+            dFreeCount *= pow(1.0 - 1.0 / 600.0, (double)(nNow - nLastTime));
+            nLastTime = nNow;
+            // -limitfreerelay unit is thousand-bytes-per-minute
+            // At default rate it would take over a month to fill 1GB
+            if(dFreeCount > GetArg("-limitfreerelay", 15) * 10 * 1000)
+              return(error("CTxMemPool::accept() : free transaction rejected by rate limiter"));
+            if(fDebug)
+              printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount + nTxSize);
+            dFreeCount += nTxSize;
         }
 
         // Check against previous transactions
@@ -624,9 +621,10 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
     return true;
 }
 
-bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs)
-{
-    return mempool.accept(txdb, *this, fCheckInputs, pfMissingInputs);
+bool CTransaction::AcceptToMemoryPool(CTxDB &txdb, bool fCheckInputs, bool fLimitFree,
+  bool *pfMissingInputs) {
+
+    return mempool.accept(txdb, *this, fCheckInputs, fLimitFree, pfMissingInputs);
 }
 
 bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
@@ -730,9 +728,9 @@ int CMerkleTx::GetBlocksToMaturity() const
 }
 
 
-bool CMerkleTx::AcceptToMemoryPool(CTxDB &txdb, bool fCheckInputs) {
+bool CMerkleTx::AcceptToMemoryPool(CTxDB &txdb, bool fCheckInputs, bool fLimitFree) {
 
-    return(CTransaction::AcceptToMemoryPool(txdb, fCheckInputs));
+    return(CTransaction::AcceptToMemoryPool(txdb, fCheckInputs, fLimitFree));
 }
 
 bool CMerkleTx::AcceptToMemoryPool()
@@ -755,10 +753,10 @@ bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs)
             {
                 uint256 hash = tx.GetHash();
                 if (!mempool.exists(hash) && !txdb.ContainsTx(hash))
-                    tx.AcceptToMemoryPool(txdb, fCheckInputs);
+                    tx.AcceptToMemoryPool(txdb, fCheckInputs, false);
             }
         }
-        return AcceptToMemoryPool(txdb, fCheckInputs);
+        return(AcceptToMemoryPool(txdb, fCheckInputs, false));
     }
     return false;
 }
@@ -1581,7 +1579,7 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
 
     // Resurrect memory transactions that were in the disconnected branch
     BOOST_FOREACH(CTransaction& tx, vResurrect)
-        tx.AcceptToMemoryPool(txdb, false);
+      tx.AcceptToMemoryPool(txdb, true, false);
 
     // Delete redundant memory transactions that are in the connected branch
     BOOST_FOREACH(CTransaction &tx, vDelete) {
@@ -3022,8 +3020,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
         }
 
         bool fMissingInputs = false;
-        if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs))
-        {
+        if(tx.AcceptToMemoryPool(txdb, true, true, &fMissingInputs)) {
             SyncWithWallets(tx, NULL, true);
             RelayMessage(inv, vMsg);
             mapAlreadyAskedFor.erase(inv);
@@ -3044,8 +3041,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
                     CInv inv(MSG_TX, tx.GetHash());
                     bool fMissingInputs2 = false;
 
-                    if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs2))
-                    {
+                    if(tx.AcceptToMemoryPool(txdb, true, true, &fMissingInputs2)) {
                         printf("   accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
                         SyncWithWallets(tx, NULL, true);
                         RelayMessage(inv, vMsg);
@@ -3053,11 +3049,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
                         vWorkQueue.push_back(inv.hash);
                         vEraseQueue.push_back(inv.hash);
                     }
-                    else if (!fMissingInputs2)
-                    {
-                        // invalid orphan
+                    else if(!fMissingInputs2) {
+                        /* Invalid or insufficient fee orphan */
                         vEraseQueue.push_back(inv.hash);
-                        printf("   removed invalid orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
+                        printf("   removed orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
                     }
                 }
             }

programming_language: C++
filename: main.h
change_type: ModificationType.MODIFY
diff: @@ -716,7 +716,8 @@ public:
                        const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash=true);
     bool ClientConnectInputs();
     bool CheckTransaction() const;
-    bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
+    bool AcceptToMemoryPool(CTxDB &txdb, bool fCheckInputs=true, bool fLimitFree=true,
+      bool *pfMissingInputs=NULL);
 
 protected:
     const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const;
@@ -771,7 +772,7 @@ public:
     int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
     bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
     int GetBlocksToMaturity() const;
-    bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true);
+    bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool fLimitFree=true);
     bool AcceptToMemoryPool();
 };
 
@@ -1721,8 +1722,8 @@ public:
     std::map<uint256, CTransaction> mapTx;
     std::map<COutPoint, CInPoint> mapNextTx;
 
-    bool accept(CTxDB& txdb, CTransaction &tx,
-                bool fCheckInputs, bool* pfMissingInputs);
+    bool accept(CTxDB &txdb, CTransaction &tx, bool fCheckInputs, bool fLimitFree,
+      bool *pfMissingInputs);
     bool addUnchecked(const uint256& hash, CTransaction &tx);
     bool remove(const CTransaction &tx, bool fRecursive = false);
     bool removeConflicts(const CTransaction &tx);

programming_language: C++
filename: rpcrawtransaction.cpp
change_type: ModificationType.MODIFY
diff: @@ -494,8 +494,8 @@ Value sendrawtransaction(const Array& params, bool fHelp)
     {
         // push to local node
         CTxDB txdb("r");
-        if (!tx.AcceptToMemoryPool(txdb))
-            throw JSONRPCError(-22, "TX rejected");
+        if(!tx.AcceptToMemoryPool(txdb, true, false))
+          throw(JSONRPCError(-22, "TX rejected"));
 
         SyncWithWallets(tx, NULL, true);
     }

programming_language: C++
filename: wallet.cpp
change_type: ModificationType.MODIFY
diff: @@ -1251,8 +1251,8 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
         mapRequestCount[wtxNew.GetHash()] = 0;
 
         // Broadcast
-        if (!wtxNew.AcceptToMemoryPool())
-        {
+        CTxDB txdb("r");
+        if(!wtxNew.AcceptToMemoryPool(txdb, true, false)) {
             // This must not fail. The transaction has already been signed and recorded.
             printf("CommitTransaction() : Error: Transaction not valid");
             return false;

programming_language: C++