-
Notifications
You must be signed in to change notification settings - Fork 6
iamnmt - Unclaimed rewards when emergency withdrawing are not redistributed in MasterChef
and MlumStaking
#460
Comments
This report was chosen as the main report because it states the findings |
When emergency withdrawing are made, Unclaimed rewards in |
MasterChef
and MlumStaking
MasterChef
and MlumStaking
The protocol team fixed this issue in the following PRs/commits: |
Escalate Some issues have been incorrectly included in the duplicates here #536 says “Loss of reward tokens during emergency withdrawing” but thats the whole point of emergency Withdraw and this has been clearly mentioned in the comments. The user is willingly letting go off rewards during an emergency acc to expected design. This is not a bug but a feature. Apart from these, the remaining issues are actually two sets of issues clubbed together incorrectly. Set 1 is about “non-redistribution of rewards of stakers who use emergencyWithdraw for exit”. The root cause is that the accRewardPerShare or lastRewardBalance are not properly adjusted so the rewards that were let go off by the staker in MasterChef and MLUMStaking are left stuck. And the fix is to adjust accRewardPerShare and lastRewardBalance accordingly in emergencyWithdraw logic or add a sweep function. Here, briber cant get back the rewards that will get stuck. Set 1 : #460, #589, #428. note that this also has another duplicate not mentioned here ie. #368 Set 2 is about the rewards lost in extraRewarder attached to a farm, if the extraRewarder is unlinked from the farm while it still stores unclaimed rewards of various stakers of that farm. Root cause is the non-existence of a direct claiming method in MasterChefRewarder contract and the fix is to add such a method. Here, the stakers will lose out on rewards they had “already earned” in the form of extra rewards. This is unrelated to the funds stuck because masterchefrewarder inherits from baserewarder which has a sweep function, but the problem here is about the deserved users unable to claim their earned rewards after unlinking. We can clearly see that these two sets are completely different according to root cause, fix ,and the code segment & actor affected. Set 2 : #342, #468, #559, #611, #649, #40 #559 is invalid because the issue description is wrong. It says “funds will be stuck forever”. But the truth is the sweep function will be used to retrieve the funds as _getTotalSupply will return 0 after becoming unlinked. So the funds are not stuck, the non-existence of a direct claim method is the problem as described above. In short, the description and impact of this submission is completely wrong, hence this is invalid. #611 is invalid because the issue description is completely wrong. As we can see in the code, if getTotalSupply returns zero, then the whole of contract’s balance will be sweeped out. And the getTotalsupply returns zero after becoming unlinked. So the rewards are not permanently stuck and can be sweeped out by owner. Instead, the problem is something different not identified by this issue. Hence, this is invalid due to wrong description and impact. #40 is invalid as it talks about something else around the rewards calculations using timestamp which is not even relevant to the context of this discussion. The Masterchef’s lastUpdateTimestamp is not relevant to masterchefRewarder contract’s distribution of "extra rewards". So Set 2 has only 3 valid issues : #342, #468, #649 Both these sets are two separate valid Medium findings |
You've created a valid escalation! To remove the escalation from consideration: Delete your comment. You may delete or edit your escalation comment anytime before the 48-hour escalation window closes. After that, the escalation becomes final. |
This should be low, code comments are the main source of truth when readme contains no information:
Additionally, this is an opportunity loss, since user who has called function _modify(uint256 pid, address account, int256 deltaAmount, bool isPayOutReward) private {
Farm storage farm = _farms[pid];
Rewarder.Parameter storage rewarder = farm.rewarder;
IMasterChefRewarder extraRewarder = farm.extraRewarder;
(uint256 oldBalance, uint256 newBalance, uint256 oldTotalSupply,) = farm.amounts.update(account, deltaAmount);
uint256 totalLumRewardForPid = _getRewardForPid(rewarder, pid, oldTotalSupply);
uint256 lumRewardForPid = _mintLum(totalLumRewardForPid);
uint256 lumReward = rewarder.update(account, oldBalance, newBalance, oldTotalSupply, lumRewardForPid);
if (isPayOutReward) {
lumReward = lumReward + unclaimedRewards[pid][account];
unclaimedRewards[pid][account] = 0;
if (lumReward > 0) _lum.safeTransfer(account, lumReward);
} So no locked funds. |
I believe this claim is not correct. In the |
Regarding the comment on issue #536, I would like to provide further clarification. While it is true that the emergency withdraw function is designed to allow users to withdraw their staked tokens immediately, forfeiting any accrued rewards, my report highlights a different concern. The issue is not about the intentional forfeiture of rewards during an emergency withdrawal, but rather the loss of unclaimed rewards due to the contract's logic. I explicitly emphasized the unclaimed rewards in the proof of concept (POC) part. Throughout the entire report, my focus was on the unclaimed rewards. I understand that forfeiting rewards during an emergency withdrawal is an intended feature, as stated in the function's NatSpec description. If my mitigation suggestion was off-target, it was because my primary concern was ensuring that unclaimed rewards were properly addressed. Therefore, I maintain that this issue should be considered a bug, as it impacts the overall integrity and expected functionality of the rewards distribution process. |
Escalate 1 : 536 incorrectly included in the duplicates here. Escalate 1 Issue number 536: If we break the basic problem and navigate inside, we will probably go to very different places, do not break away from the main picture. Escalate 2 Both issues involve losing rewards during the emergency withdrawal process. Issue 460 focuses on the failure to redistribute rewards, while issue 671 focuses on the user losing their rewards. However, in both cases, the fundamental problem is that the emergency withdrawal process does not handle rewards correctly. Both issues affect the integrity of the system. Either the failure to redistribute rewards (460) or the user losing their rewards (671) prevents the system from operating fairly and efficiently. These are two aspects of the same problem. A similar approach is required to solve both problems: correctly calculating and processing rewards during the emergency withdrawal process. This could be either giving the rewards to the user or redistributing them fairly throughout the system. The code changes that would be made to solve both problems would likely be similar. Escalate 3 All these issues (Set 1 and Set 2) are caused by deficiencies in the reward distribution mechanism. Whether it is in the emergency exit case or in the case of extraRewarder disconnection, the main problem is that users and the project cannot receive the rewards. The solutions suggested for both sets (fixing accRewardPerShare and lastRewardBalance, adding a sweep function, adding a direct request method) actually serve the same basic purpose: providing access to the rewards. Important Note: Should the project receive these rewards or should the users receive them? This part is an architectural decision and the project can decide on this, both can happen together (You probably want it to be separated into two parts because of this dilemma anyway) The code changes to be made to solve these problems require a single holistic approach. As a result, instead of evaluating these problems as separate sets, it would be better to consider them all as different aspects of the same basic problem. |
@0xSmartContract for "Escalate 3" consider that both set of problems are in different contracts altogether. While the set 1 is about MLUMStaking and MasterChef, the set 2 is about masterchefrewarder. Even if these two sets are decided to remain clubbed as one, I will provide additional arguments to prove that the other issues I listed as invalid are actually invalid. Regarding "Escalate 1" : quoted from #536 : "The absence of the harvesting mechanism in emergencyWithdraw() can lead to significant loss of accumulated rewards for users who invoke this function." which clearly is implying to the loss caused to the withdrawer themselves, which is expected behavior. These two issues #536 and #671 are saying that the withdrawer of the position himself is losing rewards because of logic not calling _harvestPosition (see the recommendation mentioned in that issue) Same goes for "Escalate 2" These two and the other ones I mentioned as invalid in original escalation Set 2 are invalid and not part of either Set 1, nor of Set 2 and not a duplicate even if these two sets remain combined into a single issue (please go over the reasoning in original escalation for why #559, #611, #40 are invalid issues wrongly validated.) |
If we break the basic issue and navigate inside, we will probably go to very different places (differently contracts , differently functions, differently recommendation, differently others), do not break away from the main picture. |
My Issue #660 also shows that the rewards fund is stucked for the user during emergency withdraw with the POC screenshot in the 2nd scenario I provided and the same issue which depicts the same Issue is this #460 and it is considered valid. So isn't it a mistaken duped rather than an invalid ? And the issue #660 is escalated and the reason given is invalid. |
1 - If we agree that the two sets remain as one, we can move on to the invalid ones ✅ 2- When I re-evaluate within the framework of your views, 611, 559 and 40 and duplicates can be considered invalid ✅ |
That is not correct. When they can't be solved with a single fix at a single place in code, and do not stem from the same root cause, how can they be duped ? Moreover, the statement "The solutions suggested for both sets (fixing accRewardPerShare and lastRewardBalance, adding a sweep function, adding a direct request method) actually serve the same basic purpose: providing access to the rewards." is actually incorrect. As i pointed out in my original escalation, set 1 is about missing re-distribution of rewards : stakers willingly forefeited their rewards by using emergencyWithdraw and these rewards now "do not belong to anyone" and thus they will get stuck because of lcak of redistribution. Set 2 is about missing way to claim rewards : stakers had earned rewards which they are "not willing to forfeit" but when extrarewarder is unlinked, they are forcefully forefeited for everyone. These rewards actually belong to these stakers as they had already accrued them in storage. (note that I'm not talking about the rewards that had not accrued). Now this is not the same as funds get stuck, because these should not be sweeped by the protocol, "they belong to the stakers" and require a direct claim method. I think that these are clearly not dups. |
I think we have made progress on the issue @chinmay-farkya's suggestion; Lead Judge suggestion ; As the Lead Judge, I appreciate your detailed analysis, but I believe there are strong reasons to consider these issues as part of a single set. Here's why: Overarching Problem: While the specific mechanisms differ, all these issues fundamentally relate to the mishandling of rewards in exceptional circumstances (emergency withdrawals or unlinking of extra rewarders). The core problem in each case is that rewards are not properly accounted for or distributed. System Impact: Both scenarios (emergency withdrawals and unlinking extra rewarders) result in a similar outcome - users potentially losing access to rewards they've accrued. This represents a systemic flaw in the reward distribution mechanism of the protocol. Holistic Solution Approach: While the exact code fixes may differ, addressing these issues requires a comprehensive review and redesign of how the protocol handles rewards in edge cases. This suggests a unified approach to solving the problem, rather than treating them as entirely separate issues. While I acknowledge that the specific mechanisms and code locations differ, I believe the similarities in the fundamental problem, impact, and required solution approach justify grouping these issues together. This holistic view allows for a more comprehensive understanding and resolution of the protocol's reward distribution challenges. Regarding the invalid issues (#559, #611, #40), I agree with your assessment. These issues either mischaracterize the problem or focus on aspects that are not directly related to the core issue of reward mishandling in exceptional circumstances. |
@0xSmartContract May I know about this issue!!! |
I'll go in the same order that the escalation comment has:
But, I need a clarification about the rewarder, is it set by the MagicSea team or anyone can set it as a Bribe rewarder for example? #660 is the same as #536 and #671, it just explains how the user loses their rewards during the emergency withdraw which is the exact purpose of this function. It does mention the loss for the protocol in the impact, but no explanation of how the protocol loses here. Hence, remains invalid. Also, excuse me for the delay here. |
It is set by the magicsea team I guess. But once they do unlink it, the impact is that the way of claiming users' rewards is immediately cut off from the extrarewarder. The admin does not have a workaround to time this action of "unlinking". This admin action harms everybody. |
dear @WangSecurity, Thank you for your consideration. |
Thank you for this clarification, but I'm afraid the following rule should apply:
In this case, the admin unlinking the rewarder leads to users losing these rewards completely. Hence, I believe the rule is applicable and the second set should be invalid.
I still stand by my words in the previous comment. #536 is only a recommendation to allow users who use Hence, I decide to accept the escalation, de-dup #536, #671, #342, #468, #559, #611, #649, #40 |
To summarize the second set of issues: Internal pre-conditions:
Vulnerability path: Admin calls to I believe that it is intended for the admin calls to
I believe the second set of issues is valid because the vulnerability path is fulfilled the note of the fifth item of the third section in the judging rules:
Also, may I ask what is the "certain assumptions about the functioning of the code" in the context of this issue? |
This statement is only true imo when the admin has a workaround alternate way of doing things : such that it would not lead to losses, and so they are trusted to instead go for this alternate way. Here, in the set 2 of issues, there is no alternate way for the admin. Setting a new extrarewarder will definitely cause loss of rewards for "all users" every time the admin uses this function, all unclaimed rewards in the previous extrarewarder contract will not be able to be claimed by users who earned them. |
It's correct, but I believe this rule is not applicable here. What I'm applying is the fifth item in the section called "List of Issue categories that are not considered valid".
In this case, the assumption is that the voters should be able to get their extra rewards even if the extraRewarder is unlinked. In fact, the admin unlinking extraRewarder leads to users losing their extra rewards.
Fair point, but I believe the rule doesn't imply anything like this. The rule is about the admin's actions causing loss of funds, e.g. pausing collateral leads to unfair liquidations (users cannot add or repay collateral) or the users losing their extra rewards if the admin unlinks the contract with extra rewards. Hence, the decision remains as expressed in this comment. The escalation will be accepted. |
So is the verdict that "admin actions that do not have an alternate workaround way and will most definitely lead to loss of funds for users : is allowed to happen and will not be treated as a bug" ? That sounds illogical imo. I will leave the decision to you, and ask for changing this rule in the future. |
Why is the rule "List of Issue categories that are not considered valid" applied but not this rule?
Invalidating the second set of issues is assuming that the admin will never trigger the This is my final comment on this issue. Hoping that the contest's rules will be updated to reflect admin related issues better. |
Because this issue is not connected to admin set variables. Here we are talking about the admin's actions, not about values that lead to an issue.
I didn't say that I invalidate this issue because "the admin will never trigger the The decision remains the same as expressed here. |
Then the rule has to be relooked imo to clarify such situations. |
Result: |
Escalations have been resolved successfully! Escalation status:
|
The Lead Senior Watson signed off on the fix. |
iamnmt
Medium
Unclaimed rewards when emergency withdrawing are not redistributed in
MasterChef
andMlumStaking
Summary
Unclaimed rewards when emergency withdrawing are not redistributed in
MasterChef
andMlumStaking
.Vulnerability Detail
Users can call
MasterChef#emergencyWithdraw
,MlumStaking#emergencyWithdraw
to withdraw tokens without claiming the rewards.But the unclaimed rewards are not redistributed in
emergencyWithdraw
call, which will left the rewards stuck in the contracts and lost forever. Other users can not claim the rewards and the protocol can not redistribute the rewards.Impact
Unclaimed rewards when emergency withdrawing in
MasterChef
andMlumStaking
will stuck in the contracts and lost forever.Code Snippet
https://github.com/sherlock-audit/2024-06-magicsea/blob/42e799446595c542eff9519353d3becc50cdba63/magicsea-staking/src/MasterchefV2.sol#L326
https://github.com/sherlock-audit/2024-06-magicsea/blob/42e799446595c542eff9519353d3becc50cdba63/magicsea-staking/src/MlumStaking.sol#L536
Tool used
Manual Review
Recommendation
Redistribute the unclaimed rewards in
emergencyWithdraw
MasterchefV2.sol
MlumStaking.sol
The text was updated successfully, but these errors were encountered: