Fixed depositor reentrancy can take all the ETH
Description
After a vault begins, fixed depositors can call the claimFixedPremium()
function to claim their ETH fixed premium:
function claimFixedPremium() external {
require(isStarted(), "CBS");
// Check and cache balance for gas savings
uint256 claimBal = fixedClaimToken[msg.sender];
require(claimBal > 0, "NCT");
// Send a proportional share of the total variable side deposits (premium) to the fixed side depositor
uint256 sendAmount = fixedETHDepositToken[msg.sender].mulDiv(variableSideCapacity, fixedSideCapacity);
// Track premiums
userToFixedUpfrontPremium[msg.sender] = sendAmount;
(bool sent, ) = msg.sender.call{value: sendAmount}("");
require(sent, "ETF");
// Mint bearer token
fixedBearerToken[msg.sender] += claimBal;
fixedBearerTokenTotalSupply += claimBal;
// Burn claim tokens
fixedClaimToken[msg.sender] = 0;
fixedClaimTokenTotalSupply -= claimBal;
emit FixedPremiumClaimed(sendAmount, claimBal, msg.sender);
}
However, since the ETH is sent before the burning of the claim tokens, if the call to msg.sender
reenters the same function, the user can claim their fixed premium again and again.
Impact
Any fixed depositor can reenter claimFixedPremium()
as many times as the gas and stack-size limit permit. By ensuring that their initial deposit is an even multiple of their claim, they can drain the entire balance using this reentrancy attack.
Additionally, this inflates the user's fixedBearerToken
, which causes them to be able to claim more of the returned fixed principal than they deserve after the vault ends.
Recommendations
Send the sendAmount
to the caller after the claim tokens are burned.
Remediation
This issue has been acknowledged by Saffron, and a fix was implemented in commit 0b1fbc2d↗.