wallet2: clamp export_outputs start to the number of transfers
What changed, and why it matters
This is a small bug fix in Monero's wallet code. When exporting transaction outputs with a special 'all=true' flag, the code could be asked to start from a position beyond the list of stored transfers. That caused a subtraction to underflow (wrap around to a huge number), which made a memory reservation throw an exception with the message 'vector::reserve'. The fix clamps the starting position to the actual number of transfers, preventing the crash and keeping the returned data consistent with what the import function expects.
Apply the patch. It is a minimal, correct bounds clamp. Consider adding an explicit unit test for export_outputs with start >= m_transfers.size() and all=true to prevent regression. No immediate incident response is indicated because the failure mode is an exception, not memory corruption or unauthorized behavior.
Security signals we found
Integer underflow in size_t arithmetic leading to exception
Unchecked user/caller supplied offset in export_outputs
std::length_error / vector::reserve crash path
Fix aligns export behavior with documented allowance for start/count past valid range
Evidence from the diff
In wallet2::export_outputs, when all=true the local offset was set directly to the caller-supplied start value without bounds checking. Because m_transfers.size() is a size_t, m_transfers.size() - offset underflows when start > m_transfers.size(). The resulting huge value is passed to std::vector::reserve, which throws std::length_error. The patch replaces ‘offset = start’ with ‘offset = std::min
Changed components
src/wallet/wallet2.cppwallet2::export_outputsInspect captured patch +1 / −1
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 19ba991..496a316 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -14014,7 +14014,7 @@ std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_det
while (offset < m_transfers.size() && (m_transfers[offset].m_key_image_known && !m_transfers[offset].m_key_image_request))
++offset;
else
- offset = start;
+ offset = std::min<size_t>(start, m_transfers.size());
outs.reserve(m_transfers.size() - offset);
for (size_t n = offset; n < m_transfers.size() && n - offset < count; ++n)
Why this scored 32/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.