The StabilityPool contract is built as an upgradeable contract using OpenZeppelin’s OwnableUpgradeable and PausableUpgradeable. However, it imports and uses the standard (non‑upgradeable) ReentrancyGuard from OpenZeppelin. Upgradeable contracts must carefully manage their storage layout. The non‑upgradeable ReentrancyGuard does not use an initializer and may occupy storage slots in a way that conflicts with upgradeable patterns. This can lead to unexpected behavior in reentrancy protection or even allow an attacker to exploit storage collisions if the upgrade process is not managed correctly.
Storage Layout Conflicts:
The non‑upgradeable ReentrancyGuard sets up its internal state (e.g. a private _status variable) in the constructor. In an upgradeable contract (which relies on initializer functions rather than constructors), the storage layout is defined by the order of inherited state variables. Mixing upgradeable base contracts with a non‑upgradeable ReentrancyGuard can lead to overlapping storage slots or uninitialized guard variables.
Exploitation Path:
Although no immediate attack vector is evident (the contract’s functions are marked nonReentrant and appear to work in tests), the risk is that if an upgrade is performed or if an attacker can force reinitialization, the reentrancy guard might not properly protect critical functions. This could open the door for reentrancy attacks on deposit, withdraw, or liquidation flows, potentially leading to loss of funds.
Maintenance Risk:
Future upgrades or changes in the storage layout may inadvertently break the intended reentrancy protection, causing subtle bugs that could be exploited in production.
Demonstrating a storage layout collision in a Foundry test can be challenging without a full proxy setup. However, we can illustrate the principle via a simplified test that shows how storage variables from non‑upgradeable contracts might be mis‑initialized when used with upgradeable contracts.
-->Note:
This test demonstrates that the contract works “as is” in a controlled environment. However, because the non‑upgradeable guard’s storage layout isn’t designed for upgradeable contracts, its use poses a maintenance risk. In a production upgradeable system, the correct approach is to use OpenZeppelin’s ReentrancyGuardUpgradeable.
Recommended Fix:
Replace the non‑upgradeable ReentrancyGuard with ReentrancyGuardUpgradeable. For example:
Using the upgradeable version ensures that storage layout is consistent with the rest of the upgradeable contracts and that the reentrancy guard is properly initialized.
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.