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.
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.
User funds are sent to unwanted urnFarm and voteDelegate addresses.
Although the user can change this after or before locking again, this actually violates the user's policy and wishes.
Before "lock" again; the user calls selectVoteDelegate and selectFarm first to address zero, then calls selectVoteDelegate and selectFarm again to the desired address. This consumes a lot of effort and time.
After "lock" again; the user calls selectVoteDelegate and selectFarm to the desired address. But if he does not want previous funds to be sent to the unwanted urnFarm and voteDelegate addresses for some reason. So, this violates the user policy.
PoC
paste the code below into LockstakeEngine.t.sol
run with forge test -vv --match-test test_lock_again_after_free
function test_lock_again_after_free() public {
address andi = makeAddr("andi");
deal(address(mkr), andi, 100_000 * 10 ** 18);
vm.startPrank(andi);
mkr.approve(address(engine), 100_000 * 10 ** 18);
address urnAndi = engine.open(0);
address voteDelegate_andi = voteDelegateFactory.create();
engine.lock(urnAndi, 100_000 * 10 ** 18, 5);
// lock to voteDelegate_andi
engine.selectVoteDelegate(urnAndi, voteDelegate_andi);
assertEq(mkr.balanceOf(voteDelegate_andi), 100_000 * 10 ** 18);
engine.free(urnAndi, andi, 100_000 * 10 ** 18);
assertEq(mkr.balanceOf(voteDelegate_andi), 0);
console.log("andi's fund in old voteDelegate after free:", mkr.balanceOf(voteDelegate_andi));
deal(address(mkr), andi, 100_000 * 10 ** 18);
mkr.approve(address(engine), 100_000 * 10 ** 18);
engine.lock(urnAndi, 100_000 * 10 ** 18, 5);
assertEq(mkr.balanceOf(voteDelegate_andi), 100_000 * 10 ** 18);
console.log("andi lock again, but the funds sent were to the old voteDelegate:", mkr.balanceOf(voteDelegate_andi));
}
Mitigation
Reset urnFarm and voteDelegate addresses after free
Laksmana
Medium
Cache issue, user's funds are sent to the unintended
urnFarm
andvoteDelegate
.Summary
Missing resetting of
urnFarm
andvoteDelegate
addresses after doingfree
.Causing users' funds to be sent to unwanted
urnFarm
andvoteDelegate
addresses. when they want tolock
again after doingfree
, but not to lock or stake on the previousurnFarm
andvoteDelegate
.Root Cause
Look at this function _free:
Look at the code, there is no resetting
urnFarm
andvoteDelegate
to address(0) after "withdraw" and "free".So, when the user does
free
whenurnFarm
andvoteDelegate
are not at address(0) / where the user has funds inurnFarm
andvoteDelegate
, it will "withdraw" and "free" the user's funds inurnFarm
andvoteDelegate
, but after that it is not reset to address(0).Then, when the user does
lock
again. It will send user funds to the previousurnFarm
andvoteDelegate
addresses without user permission.Check this out _lock:
Internal pre-conditions
No response
External pre-conditions
No response
Attack Path
No response
Impact
User funds are sent to unwanted
urnFarm
andvoteDelegate
addresses.Although the user can change this after or before locking again, this actually violates the user's policy and wishes.
Before "lock" again; the user calls
selectVoteDelegate
andselectFarm
first to address zero, then callsselectVoteDelegate
andselectFarm
again to the desired address. This consumes a lot of effort and time.After "lock" again; the user calls
selectVoteDelegate
andselectFarm
to the desired address. But if he does not want previous funds to be sent to the unwantedurnFarm
andvoteDelegate
addresses for some reason. So, this violates the user policy.PoC
run with
forge test -vv --match-test test_lock_again_after_free
Mitigation
urnFarm
andvoteDelegate
addresses after free