Skip to content
Request an audit

‹ All findings

A malicious user may unlock instantly all the funds from the FluidLocker when no one is staking in the Tax pool

MediumSuperfluid Locker System·Sherlock · Streaming · 20th November 2024CodebaseM-3

Summary

The missing check in FluidLocker::_instantUnlock() for stakers in the tax pool allows users to unlock all their funds without paying any tax instantly. This happens because whenever there are 0 stakers in the tax pool, the flow rate is set to 0 and it does not revert, so the user can loop unlocking until all funds are withdrawn.

Note: FluidEPProgramManager::stopFunding() also has issues related to 0 stakers in the program or tax pools.

Note2: the mentioned assumptions in the readme about assuming there are stakers before calls only refer to FluidEPProgramManager::startFunding() and Fountaine::initialize(), not these 2 flows mentioned here.

Root Cause

In FluidLocker::_instantUnlock() there is a missing check for 0 stakers in the tax pool.

Internal pre-conditions

  1. There are 0 stakers in the tax pool.

External pre-conditions

None.

Attack Path

  1. User loops FluidLocker::unlock() with a null unlocking period and instantly withdraws all their funds.

Impact

The user is able to unlock all their funds without paying any tax.

PoC

Add the following test to FluidLocker.t.sol.

solidity
function test_POC_InstantUnlock_WithoutFees() external {
    _helperFundLocker(address(aliceLocker), 10_000e18);

    assertEq(_fluidSuperToken.balanceOf(address(ALICE)), 0, "incorrect Alice bal before op");
    assertEq(_fluidSuperToken.balanceOf(address(aliceLocker)), 10_000e18, "incorrect Locker bal before op");

    _helperUpgradeLocker();

    vm.startPrank(ALICE);
    for (uint i = 0; i < 30; i++) {
        aliceLocker.unlock(0, ALICE);
    }

    assertGt(_fluidSuperToken.balanceOf(address(ALICE)), 9.98e21, "incorrect Alice bal after op");
}

Mitigation

Check if the pools have 0 units and revert if so.