Fix crowdloan reentrancy bug#2786
Conversation
🛡️ AI Review — Skeptic (security review)VERDICT: SAFE BASELINE scrutiny: established 2018 account, repo write permission, substantial prior subtensor PR history, no Gittensor allowlist match; branch fix-crowdloan-reentrancy -> devnet-ready. No Static review found the reentrancy guards are placed before fund-moving paths: FindingsNo findings. Prior-comment reconciliation
ConclusionNo malicious behavior or Skeptic-level security vulnerability found in the current diff. The patch closes the reentrancy window without adding a new bypass or persistent failed-finalize lock. 🔍 AI Review — Auditor (domain review)VERDICT: 👎 Gittensor: UNKNOWN; author is an established repo contributor with write permission. Duplicate-work check found no better crowdloan candidate among overlapping PRs. PR body is substantive and matches the intended fix. Validation: Findings
Prior-comment reconciliation
ConclusionBlocking until |
|
🔄 AI review updated — Skeptic: VULNERABLE |
|
🔄 AI review updated — Skeptic: VULNERABLE |
| ensure!(crowdloan.raised == crowdloan.cap, Error::<T>::CapNotRaised); | ||
| ensure!(!crowdloan.finalized, Error::<T>::AlreadyFinalized); | ||
|
|
||
| crowdloan.finalized = true; |
There was a problem hiding this comment.
[HIGH] Make finalize transactional before setting finalized early
This write now happens before several fallible operations: preimage lookup/drop, dispatched call execution, balance transfer, and invalid finalization config. Because finalize itself is not annotated #[transactional] or wrapped in an equivalent storage transaction, a later Err can leave crowdloan.finalized = true persisted without emitting Finalized or moving funds. That turns transient failures, including a re-entrant AlreadyFinalized, into a permanent lock that also blocks withdraw, refund, updates, and dissolve paths.
The new test_finalize_blocks_reentrant_withdraw wraps the call in frame_support::storage::with_storage_layer, but that only changes the test harness; production finalize still has no transaction boundary. Add use frame_support::transactional; and #[transactional] to finalize, or keep the early guard in a transactional helper, so any post-marking error rolls this storage write back.
|
🔄 AI review updated — Skeptic: VULNERABLE |
|
One question: if the withdraw and refund cann't be dispatched successfully, should we check both types of all in create extrinsic |
|
🔄 AI review updated — Skeptic: VULNERABLE |
|
🔄 AI review updated — Skeptic: VULNERABLE |
|
🔄 AI review updated — Skeptic: SAFE Auditor: 👎 |
|
needs spec bump and a few things |
|
🔄 AI review updated — Skeptic: SAFE Auditor: 👍 |
|
🔄 AI review updated — Skeptic: SAFE Auditor: 👎 |
Summary
AlreadyFinalizedguard before moving funds.AlreadyFinalizingguard to reject nested finalize calls whileCurrentCrowdloanIdis set, preserving the invariant that only one crowdloan finalization context can be active at a time.AlreadyFinalizingis returned and both crowdloans remain unfinalized