The nonReentrant
modifier is ineffective due to an incorrect implementation using assembly. The intended reentrancy protection mechanism can be bypassed, making the contract vulnerable to reentrancy attacks.
The modifier uses storage slot 0
to store the reentrancy lock, but it loads from slot 1
, creating an inconsistency.
The check if tload(1) { revert(0, 0) }
is ineffective because it never reads the correct storage location.
This means an attacker can call the function recursively without triggering the expected revert condition, allowing reentrancy exploits.
The first assembly block checks slot 1
, but the lock is set in slot 0
, making the check ineffective.
As a result, the function can be reentered because the revert(0, 0)
statement never executes under normal conditions
Complete bypass of reentrancy protection, making functions vulnerable to reentrancy attacks where an attacker can drain funds or manipulate contract state.
High risk in financial applications, especially if the function deals with ETH transfers, token approvals, or state changes that assume a single execution flow.
Potential contract compromise if attackers exploit reentrancy to drain assets or disrupt operations.
Manual Review
Modify the modifier to use the same slot for both reading and writing:
CopyEdit
modifier nonReentrant() { assembly { if tload(0) { revert(0, 0) } // ✅ Correctly loads from slot 0 tstore(0, 1) // ✅ Sets lock in the same slot } _; assembly { tstore(0, 0) // ✅ Unlocks correctly } }
If possible, use OpenZeppelin’s ReentrancyGuard
, which is a battle-tested approach:
CopyEdit
import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract MyContract is ReentrancyGuard { function myFunction() external nonReentrant { // Safe from reentrancy } }
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.