Christmas Dinner

First Flight #31
Beginner FriendlyFoundrySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

Ineffective Reentrancy Protection in `nonReentrant` Modifier

Summary

The nonReentrant modifier is intended to prevent reentrancy attacks by implementing a mutex lock. However, the current implementation does not properly set the locked variable to true before executing the function body. As a result, the reentrancy protection is ineffective, leaving functions like ChristmasDinner::refund() and its associated internal calls (ChristmasDinner::_refundERC20 and ChristmasDinner::_refundETH) vulnerable to reentrancy attacks.

Vulnerability Details

Code in question :

modifier nonReentrant() {
require(!locked, "No re-entrancy");
_;
locked = false;
}

The modifier checks that locked is false before executing the function.
However, it does not set locked = true before the function body executes (_;), leaving the contract unprotected during function execution.
The modifier does set locked = false after execution, but this is redundant since locked is already false by default and remains unchanged during execution.

The modifier is used only on ChristmasDinner::refund() which subsequently calls both ChristmasDinner::_refundERC20 and ChristmasDinner::_refundETH functions that directly interact with user balances and can transfer funds. Without proper reentrancy protection, an attacker could call refund() recursively, exploiting the state changes and draining funds.

Impact

The ineffective nonReentrant modifier fails to protect critical functions like refund(), which could allow an attacker to recursively drain funds.
Participants’ balances could be depleted by malicious actors, causing financial harm to users and loss of trust in the contract.
The presence of the nonReentrant modifier in ChristmasDinner::refund(), mentioned also several times in the natspec as 'not needed here, because already implemented @...', creates a false sense of security.

Tools Used

Manual code review

Recommendations

Update the modifier to set locked = true before executing the function body:

modifier nonReentrant() {
require(!locked, "No re-entrancy");
+ ---> locked = true; // Fix: Set the lock before function execution
_;
locked = false; // Reset the lock after function execution
}
Updates

Lead Judging Commences

0xtimefliez Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

mutex lock incomplete

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!