-
-
Notifications
You must be signed in to change notification settings - Fork 369
Spark minting access violation #1767
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
Cursor Agent can help with this pull request. Just |
|
bugbot run |
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. WalkthroughCaptured chain height under cs_main for consistent nLockTime; moved Spark fee/coin-selection and spend construction into wallet+chain locks; refined per-recipient fee distribution and runtime checks; corrected multiple LogPrintf/printf format specifiers across miner, mempool, validation, wallet, and libspark files. Changes
Sequence Diagram(s)mermaid Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/miner.cpp (1)
1165-1172: Fix format specifier mismatches in logging statements.These lines pass non-string types to
%sformat specifiers, which can cause crashes or undefined behavior in tinyformat:Specifier mismatches
pindexPrev->nHeightisint, not stringpblock->nVersionisint32_t, not stringpblock->nTimeisuint32_t, not stringpblock->nNonceisuint32_t(currently also incorrectly passed as pointer with&)Proposed fix
- LogPrintf("pindexPrev->nHeight: %s\n", pindexPrev->nHeight); + LogPrintf("pindexPrev->nHeight: %d\n", pindexPrev->nHeight); LogPrintf("pblock: %s\n", pblock->ToString()); - LogPrintf("pblock->nVersion: %s\n", pblock->nVersion); - LogPrintf("pblock->nTime: %s\n", pblock->nTime); - LogPrintf("pblock->nNonce: %s\n", &pblock->nNonce); + LogPrintf("pblock->nVersion: %d\n", pblock->nVersion); + LogPrintf("pblock->nTime: %u\n", pblock->nTime); + LogPrintf("pblock->nNonce: %u\n", pblock->nNonce);
🤖 Fix all issues with AI agents
In `@src/miner.cpp`:
- Around line 1109-1112: The LogPrintf in the coinbaseScript check dereferences
coinbaseScript->reserveScript.empty() causing a crash when coinbaseScript is
null; update the if block (around the coinbaseScript check in miner.cpp, where
coinbaseScript and its reserveScript are inspected) to avoid dereferencing when
coinbaseScript is null by logging coinbaseScript pointer and conditionally
logging reserveScript.empty() only if coinbaseScript is non-null (use a ternary
or separate branches) before throwing the std::runtime_error.
In `@src/txmempool.cpp`:
- Around line 521-524: The log message in the tx removal branch is misleading:
you're logging "IsSpend()=%d" while printing the result of HasPrivateInputs(),
and that value is always false in this branch; update the LogPrintf call in the
block that handles !it->GetTx().HasPrivateInputs() to either (a) change the
label to "HasPrivateInputs()=%d" so it matches the printed value, or (b) remove
the redundant boolean argument entirely and simplify the message (e.g.,
"removeUnchecked txHash=%s (no private inputs)")—locate the LogPrintf invocation
near the check of it->GetTx().HasPrivateInputs() and adjust the format string
and arguments accordingly.
🧹 Nitpick comments (1)
src/spark/sparkwallet.cpp (1)
852-853: Correct fix for format specifier.Changing
nFeeRettoCAmountand the format specifier from%sto%dcorrectly addresses the access violation. However, loggingnFeeRetwhen it's always 0 at this point seems like leftover debug code.Consider removing or relocating the debug log
If this log is for debugging, consider removing it or moving it to where
nFeeRethas a meaningful value:CAmount nFeeRet = 0; - LogPrintf("nFeeRet=%d\n", nFeeRet);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
|
Security Audit Report: PR #1767 (Spark Minting Access Violation) Critical Findings (Bugs Being Fixed)
Fix Applied: Uses std::fill() to zero buffers instead of clear(), maintaining buffer size. Security Impact: The original code caused heap/stack corruption that could manifest as crashes with corrupted stack traces. This could theoretically be exploited for memory manipulation attacks. The fix properly zeros sensitive cryptographic material before reuse. Assessment: ✅ Legitimate critical security fix
Fix Applied: Complete rewrite with proper bounds checking and no mid-loop erasure. Security Impact: This could cause wallet crashes and potentially memory corruption when anonymizing funds. Assessment: ✅ Legitimate critical fix
Fix Applied: Added if (decoded.hrp.size() < 2) check before access. Security Impact: Malformed address strings could cause out-of-bounds reads, potentially leaking memory or causing crashes. Assessment: ✅ Legitimate security fix
Fix Applied: Added && !remainingOutputs.empty() condition. Security Impact: Prevents dereferencing invalid iterators, which could cause crashes or memory reads from invalid locations. Assessment: ✅ Legitimate fix
Fix Applied: Uses ternary operator to only dereference when non-null. Security Impact: Prevents miner crash on null pointer dereference. Assessment: ✅ Legitimate fix
Fix Applied: Proper locking with LOCK(cs_main) before access. Security Impact: Prevents potential race conditions that could lead to inconsistent state or crashes. Assessment: ✅ Legitimate thread-safety fix
Fix Applied: Added conditional: recipientsToSubtractFee > 0 ? fee / recipientsToSubtractFee : 0 Security Impact: Prevents potential division by zero crashes. Assessment: ✅ Legitimate fix
Fix Applied: Corrected to "-change" Security Impact: Would cause std::out_of_range exception when -change argument is used. Assessment: ✅ Legitimate bug fix
Fix Applied: Changed to appropriate specifiers (%d, %u, %zu, %p). Security Impact: tinyformat would interpret integer values as memory addresses, causing access violations. While primarily a stability issue, malformed log inputs could theoretically be used in format string attacks. Assessment: ✅ Legitimate fixes Potential Concerns (Minor) Error Message Change: "Signing transaction failed" → "Transaction not allowed in mempool" is more accurate but could affect any code parsing error strings. Risk: Minimal Behavioral Change in Fee Distribution: The new fee subtraction gives the remainder to the first output unconditionally, whereas the old (broken) code had more complex logic. The new behavior is acceptable and the old code was non-functional anyway. Risk: None Items NOT Found (Negative Findings) |
This commit includes the PR #1766 fixes plus additional format specifier corrections to prevent EXCEPTION_ACCESS_VIOLATION crashes: Spark minting fixes (from PR #1766): - Fix nFeeRet format specifier from %s to %d in sparkwallet.cpp - Change nFeeRet type from auto to CAmount for proper formatting - Fix nFeeRet format specifier in wallet.cpp CreateLelantusMintTransactions - Add locking for chainActive.Height() access - Rework fee subtraction logic to prevent underflows - Add validation for fee subtraction in spark spend Additional format specifier fixes: - Fix payTxFee.GetFeePerK()=%s to %d in wallet.cpp (2 locations) - Fix listOwnCoins.size()=%s to %zu in wallet.cpp - Fix CheckFinalTx and IsTrusted boolean format from %s to %d - Fix nDepth=%s to %d in wallet.cpp - Fix pcoin->tx->vout.size()=%s to %zu in wallet.cpp - Fix nHeight format specifiers in validation.cpp (6 locations) - Fix isVerifyDB format from %s to %d in validation.cpp - Fix coinbaseScript pointer and boolean format in miner.cpp - Fix HasPrivateInputs boolean format in txmempool.cpp - Fix vecOutputs.size() and coins.size() in rpcwallet.cpp The root cause was using %s format specifier with non-string types (int, size_t, bool, CAmount), causing tinyformat to interpret integer values as memory addresses, leading to access violations. Co-authored-by: reuben <reuben@firo.org>
When coinbaseScript is NULL, the LogPrintf was unconditionally calling coinbaseScript->reserveScript.empty() which would crash. Use a ternary operator to only dereference when coinbaseScript is non-null. Co-authored-by: reuben <reuben@firo.org>
The log was printing 'IsSpend()=%d' but actually calling HasPrivateInputs(), and since we're inside the !HasPrivateInputs() block, the value was always false. Simplified to a clearer message that describes the branch condition. Co-authored-by: reuben <reuben@firo.org>
coinbaseScript is a boost::shared_ptr<CReserveScript>, not a raw pointer. Using %p with a shared_ptr causes tinyformat to fail. Use .get() to extract the raw pointer for logging. Co-authored-by: reuben <reuben@firo.org>
- pindexPrev->nHeight: %s -> %d (int) - pblock->nVersion: %s -> %d (int) - pblock->nTime: %s -> %u (uint32_t) - pblock->nNonce: %s with &address -> %u with value (was passing address of nNonce!) Co-authored-by: reuben <reuben@firo.org>
The fee subtraction loop had multiple bugs: 1. After erase(), singleTxOutputs[i] referred to the wrong element (shifted) 2. If erasing the last element, singleTxOutputs[i] was out of bounds 3. The subtraction singleTxOutputs[i].v -= singleFee ran even after erase This caused undefined behavior (memory corruption/crashes) when: - Clicking 'Anonymize all' button - Sending from transparent balance to Spark address Fixed by using the same safe pattern as CreateSparkSpendTransaction: - Check for empty vector upfront - Calculate fee per output and remainder - First output pays the remainder - Validate each amount before subtraction (no erasing mid-loop) Co-authored-by: reuben <reuben@firo.org>
Prevent potential crash if remainingOutputs becomes empty while remainingMintValue is still > 0. This could happen in edge cases and would cause dereferencing of an invalid iterator. Co-authored-by: reuben <reuben@firo.org>
Bug fixes in sparkwallet.cpp:
1. Fix typo: mapMultiArgs.at("change") -> mapMultiArgs.at("-change")
This would cause std::out_of_range exception when -change arg is used.
2. Add empty check before utxos.second.front() access to prevent
undefined behavior if any entry has an empty vector.
3. Add break after finding matching UTXO entry to avoid unnecessary
iterations and potential issues.
4. Fix missing strFailReason when SelectCoins fails for reasons other
than insufficient funds - now sets "Unable to select coins for minting".
5. Fix missing strFailReason when function fails because valueToMint > 0
at end - now sets descriptive error message.
6. Fix misleading error message: "Signing transaction failed" changed to
"Transaction not allowed in mempool" for mempool rejection.
Bug fix in wallet.cpp:
- Same typo fix for mapMultiArgs.at("change") in CreateLelantusMintTransactions
These bugs could potentially cause crashes (especially #1 and #2) or
make debugging difficult due to missing/wrong error messages.
Co-authored-by: reuben <reuben@firo.org>
1. SpendKey constructor: After data.clear() and result.clear(), the code was accessing data.data() and result[0] on empty vectors, causing undefined behavior. This could corrupt memory and cause crashes with bizarre stack traces (explaining why boost::shared_ptr<CReserveScript> appeared in spark minting crash traces - corrupted memory was being misinterpreted). Fix: Use std::fill() to zero the buffers instead of clear(), maintaining the vector size and valid memory. 2. Address::decode: Accessing decoded.hrp[0] and decoded.hrp[1] without checking if hrp has at least 2 characters could cause out-of-bounds access. Fix: Add size check before accessing hrp elements. These bugs could cause heap/stack corruption that manifests as crashes during seemingly unrelated operations (like Spark minting), with corrupted stack traces showing wrong function names and types. Co-authored-by: reuben <reuben@firo.org>
Replace std::fill with memory_cleanse in SpendKey constructor to securely clear cryptographic key material. memory_cleanse uses OPENSSL_cleanse which is guaranteed not to be optimized away by the compiler, providing proper protection against cold boot attacks and memory dump analysis. Co-authored-by: reuben <reuben@firo.org>
c4fde15 to
7146c97
Compare
This pull request contains changes generated by a Cursor Cloud Agent
Note
Strengthens Spark mint/spend transaction creation and improves safety/diagnostics.
nLockTimeundercs_main, fix fee subtraction (bounds checks, remainder handling, empty-output guard), improve coin selection errors, prevent invalid iterator use, and return clearer failure reasons; ensure change UTXO handling checks for empties; propagate mempool rejection reasonsnLockTimeFinalize(...data()); zero buffers between s1/s2 derivations;Address::decodenow validates HRP length before prefix checkLogPrintfformat specifiers, safer pointer loggingThese changes focus on correctness, thread-safety, and better error reporting without altering public APIs.
Written by Cursor Bugbot for commit c4fde15. This will update automatically on new commits. Configure here.