Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

Security Analysis: Nonreentrant Lock Using Wrong Storage Slot

Summary

The nonReentrant modifier in the InheritanceManager contract contains a critical vulnerability where the reentrancy lock checks one transient storage slot but modifies a different one. This mismatch renders the reentrancy protection completely ineffective, allowing malicious contracts to perform reentrancy attacks on functions that should be protected.

Vulnerability Details

The vulnerable modifier is implemented as follows:

modifier nonReentrant() {
assembly {
if tload(1) { revert(0, 0) }
tstore(0, 1)
}
_;
assembly {
tstore(0, 0)
}
}

The issue is that the modifier:

  1. Checks if slot 1 is non-zero with tload(1)

  2. But sets slot 0 to 1 with tstore(0, 1) before executing the function

  3. And resets slot 0 to 0 with tstore(0, 0) after execution

This mismatch between checking slot 1 but modifying slot 0 means the lock is never actually enforced. Since slot 1 is never modified by the contract, the if tload(1) condition will always evaluate to false, allowing multiple reentrant calls to pass through.

This is particularly concerning because the comment explicitly states that this is a "gas efficient cross-function reentrancy lock using transient storage," implying it should be secure against reentrancy attacks.

Impact

The impact of this vulnerability is critical:

  1. Complete Reentrancy Protection Failure: Any function using this modifier is vulnerable to reentrancy attacks despite the intended protection.

  2. Critical Functions Exposed: Several sensitive functions use this modifier:

    • sendERC20: Could be exploited to drain ERC20 tokens

    • sendETH: Could be exploited to drain ETH

    • contractInteractions: Could be exploited to perform multiple malicious contract calls

  3. False Sense of Security: The presence of the modifier suggests protection that doesn't exist, which could lead to less scrutiny during code review.

  4. Complex Attack Vectors: Since contractInteractions allows arbitrary contract interactions with ETH transfer, an attacker could craft a malicious contract that reenters the InheritanceManager contract via this function.

Tools Used

Manual code review.

Recommendations

  1. Fix the slot mismatch by using consistent transient storage slots:

modifier nonReentrant() {
assembly {
if tload(1) { revert(0, 0) }
tstore(1, 1)
}
_;
assembly {
tstore(1, 0)
}
}
  1. Alternatively, implement the nonReentrant modifier using standard Solidity without assembly for improved readability and maintainability:

// Add this state variable
uint256 private locked = 1;
modifier nonReentrant() {
require(locked == 1, "ReentrancyGuard: reentrant call");
locked = 2;
_;
locked = 1;
}
  1. If transient storage is preferred for gas optimization, consider using a more explicit implementation:

modifier nonReentrant() {
uint256 currentLockValue;
assembly {
currentLockValue := tload(1)
}
require(currentLockValue == 0, "ReentrancyGuard: reentrant call");
assembly {
tstore(1, 1)
}
_;
assembly {
tstore(1, 0)
}
}
  1. Add comprehensive tests specifically targeting reentrancy protection to ensure the fixed implementation works as expected.

Updates

Lead Judging Commences

0xtimefliez Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Wrong value in nonReentrant modifier

Support

FAQs

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