wallet2: check reserve proof additional key index
What changed, and why it matters
This patch tightens a safety check in Monero's wallet when creating a 'reserve proof' (a cryptographic receipt proving funds exist). Previously the wallet only checked whether any extra public keys existed at all; now it checks whether the specific key needed for the output actually exists. Without this check, the wallet could read past the end of an empty list and crash or behave unpredictably, but the change is small and the error path already threw an exception.
Apply the patch. It is a minimal, defensive fix. Users relying on reserve proofs should upgrade, though no active remote exploit path is evident from the diff alone.
Security signals we found
Bounds-check hardening on array index before use
Out-of-bounds read risk in reserve-proof generation
Potential use of invalid tx pub key pointer
Reported by external party (xmrack / MAGIC Monero Fund)
Evidence from the diff
In wallet2::get_reserve_proof(), the code iterates over normal and additional transaction public keys to derive the expected output key. The old guard threw only if additional_tx_pub_keys was empty before indexing additional_tx_pub_keys[proof.index_in_tx]. The new guard throws if proof.index_in_tx is out of bounds for additional_tx_pub_keys. This prevents a potential out-of-bounds read / use of an invalid key pointer when the additional key list is non-empty but shorter than the requested index, or when the list is empty (size 0 >= 0 is true, so it still throws).
Changed components
src/wallet/wallet2.cppwallet2::get_reserve_proof()reserve proof generationInspect captured patch +2 / −2
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 1e69262..a0191e6 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -12741,8 +12741,8 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t,
error::wallet_internal_error, "Failed to derive subaddress public key");
if (m_subaddresses.count(subaddress_spendkey) == 1)
break;
- THROW_WALLET_EXCEPTION_IF(additional_tx_pub_keys.empty(), error::wallet_internal_error,
- "Normal tx pub key doesn't derive the expected output, while the additional tx pub keys are empty");
+ THROW_WALLET_EXCEPTION_IF(proof.index_in_tx >= additional_tx_pub_keys.size(), error::wallet_internal_error,
+ "Normal tx pub key doesn't derive the expected output, and no additional tx pub key exists for this output index");
THROW_WALLET_EXCEPTION_IF(i == 1, error::wallet_internal_error,
"Neither normal tx pub key nor additional tx pub key derive the expected output key");
tx_pub_key_used = &additional_tx_pub_keys[proof.index_in_tx];
Why this scored 57/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.