blockchain_utilities: fix memory safety bugs
What changed, and why it matters
This commit fixes two memory-safety bugs in Monero's command-line blockchain utility tools. One bug could let a crafted saved state file cause the program to allocate an enormous vector and crash or exhaust memory. The other bug could cause the program to read a transaction input incorrectly and crash when computing ring-size statistics. These are offline analysis tools, not the live Monero daemon or wallet, so they do not directly affect running nodes or user funds.
Treat as a routine security hardening fix. Users who run blockchain_ancestry or blockchain_stats with untrusted state files or blockchain data should update. No emergency action is needed for daemon, wallet, or network operators because these are offline utilities.
Security signals we found
Memory allocation based on attacker-controlled deserialized data without bounds checking
Unchecked variant access (boost::get) on transaction inputs
Crash/DoS potential in offline blockchain utilities
Commit title explicitly labels changes as 'fix memory safety bugs'
Evidence from the diff
In blockchain_ancestry.cpp, deserialization of an unordered_map into a vector used old_block_cache.size() as the resize target, then indexed by block height. A malicious or corrupt state file with a very large height key would create a sparse vector of that size, causing excessive memory allocation. The patch computes the maximum height, validates it against max_size(), and resizes to max_height+1. In blockchain_stats.cpp, ring-size computation unconditionally called boost::get on tx.vin[0] without checking that the vector is non-empty or that the variant type is txin_to_key; a non-matching or empty input would throw a boost::bad_get exception. The patch adds guards.
Changed components
src/blockchain_utilities/blockchain_ancestry.cppsrc/blockchain_utilities/blockchain_stats.cppInspect captured patch +7 / −2
diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp
index 721aec7..736c6ee 100644
--- a/src/blockchain_utilities/blockchain_ancestry.cpp
+++ b/src/blockchain_utilities/blockchain_ancestry.cpp
@@ -39,6 +39,7 @@
#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_core/cryptonote_core.h"
#include "version.h"
+#include "misc_log_ex.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "bcutil"
@@ -158,7 +159,11 @@ struct ancestry_state_t
{
std::unordered_map<uint64_t, cryptonote::block> old_block_cache;
a & old_block_cache;
- block_cache.resize(old_block_cache.size());
+ uint64_t max_height = 0;
+ for (const auto& i: old_block_cache)
+ max_height = std::max(max_height, i.first);
+ CHECK_AND_ASSERT_THROW_MES(old_block_cache.empty() || max_height < block_cache.max_size(), "Corrupt ancestry state: block height too large");
+ block_cache.resize(old_block_cache.empty() ? 0 : max_height + 1);
for (const auto& i: old_block_cache)
block_cache[i.first] = i.second;
}
diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp
index 372c8f6..aa6a1a9 100644
--- a/src/blockchain_utilities/blockchain_stats.cpp
+++ b/src/blockchain_utilities/blockchain_stats.cpp
@@ -334,7 +334,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
maxins = io;
totins += io;
}
- if (do_ringsize) {
+ if (do_ringsize && !tx.vin.empty() && tx.vin[0].type() == typeid(cryptonote::txin_to_key)) {
const cryptonote::txin_to_key& tx_in_to_key
= boost::get<cryptonote::txin_to_key>(tx.vin[0]);
io = tx_in_to_key.key_offsets.size();
Why this scored 42/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.