sherlock-audit / 2024-06-makerdao-endgame-judging

1 stars 1 forks source link

Laksmana - Cache issue, user's funds are sent to the unintended ``urnFarm`` and ``voteDelegate``. #103

Closed sherlock-admin2 closed 1 month ago

sherlock-admin2 commented 1 month ago

Laksmana

Medium

Cache issue, user's funds are sent to the unintended urnFarm and voteDelegate.

Summary

Missing resetting of urnFarm and voteDelegate addresses after doing free.

Causing users' funds to be sent to unwanted urnFarm and voteDelegate addresses. when they want to lock again after doing free, but not to lock or stake on the previous urnFarm and voteDelegate.

Root Cause

Look at this function _free:

function _free(address urn, uint256 wad, uint256 fee_) internal returns (uint256 freed) {
        require(wad <= uint256(type(int256).max), "LockstakeEngine/overflow");
        address urnFarm = urnFarms[urn];
        if (urnFarm != address(0)) {
=>            LockstakeUrn(urn).withdraw(urnFarm, wad);
        }
        lsmkr.burn(urn, wad);
        vat.frob(ilk, urn, urn, address(0), -int256(wad), 0);
        vat.slip(ilk, urn, -int256(wad));
        address voteDelegate = urnVoteDelegates[urn];
        if (voteDelegate != address(0)) {
=>            VoteDelegateLike(voteDelegate).free(wad);
        }
        uint256 burn = wad * fee_ / WAD;
        if (burn > 0) {
            mkr.burn(address(this), burn);
        }
        unchecked { freed = wad - burn; } // burn <= wad always
    }

Look at the code, there is no resetting urnFarm and voteDelegate to address(0) after "withdraw" and "free".

So, when the user does free when urnFarm and voteDelegate are not at address(0) / where the user has funds in urnFarm and voteDelegate, it will "withdraw" and "free" the user's funds in urnFarm and voteDelegate, but after that it is not reset to address(0).

Then, when the user does lock again. It will send user funds to the previous urnFarm and voteDelegate addresses without user permission.

Check this out _lock:

function _lock(address urn, uint256 wad, uint16 ref) internal {
        require(urnOwners[urn] != address(0), "LockstakeEngine/invalid-urn");
        require(wad <= uint256(type(int256).max), "LockstakeEngine/overflow");
        address voteDelegate = urnVoteDelegates[urn];
        if (voteDelegate != address(0)) {
            mkr.approve(voteDelegate, wad);
=>            VoteDelegateLike(voteDelegate).lock(wad);
        }
        vat.slip(ilk, urn, int256(wad));
        vat.frob(ilk, urn, urn, address(0), int256(wad), 0);
        lsmkr.mint(urn, wad);
        address urnFarm = urnFarms[urn];
        if (urnFarm != address(0)) {
            require(farms[urnFarm] == FarmStatus.ACTIVE, "LockstakeEngine/farm-deleted");
=>            LockstakeUrn(urn).stake(urnFarm, wad, ref);
        }
    }

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

No response

Impact

PoC

Mitigation

telome commented 1 month ago

Not an issue. Intended behaviour.