The _unsettled() function does not check to see if theproduct.position(account) function returns a valid number. This means that an attacker could call the _unsettled() function with a negative position, this allow the attacker to steal rewards from the contract.
Vulnerability Detail
*/
function _unsettled(
Program storage self,
IProduct product,
ProgramInfo memory programInfo,
address account,
IOracleProvider.OracleVersion memory currentOracleVersion
) private view returns (UFixed18 amount) {
// program stage overview
//
// V = latest user settle version, V' = current user settle version
// S = versionStarted, E = versionEnded
//
// (1) V V' S E program not yet started
// (2) V S V' E use versionStarted -> V' for userShareDelta
// (3) S V V' E use V -> V' for userShareDelta
// (4) S V E V' use V -> versionComplete for userShareDelta
// (5) S E V V' program already completed
// (6) V S E V' use versionStarted -> versionComplete for userShareDelta
//
// NOTE: V == S and V' == E both default to the inner case
(uint256 _versionStarted, uint256 _versionComplete) = (
self.versionStarted == 0 ? currentOracleVersion.version : self.versionStarted, // start must be no earlier than current version
self.versionComplete == 0 ? type(uint256).max : self.versionComplete // we don't know when completion occurs
);
// accruing must start between self.versionStarted and self.versionComplete
uint256 fromVersion = Math.min(_versionComplete, Math.max(_versionStarted, product.latestVersion(account)));
// accruing must complete between self.versionStarted and self.versionComplete, we know self.versionStarted must be no earlier than current version
uint256 toVersion = Math.min(_versionComplete, currentOracleVersion.version);
Accumulator memory globalShareDelta = product.shareAtVersion(toVersion).sub(product.shareAtVersion(fromVersion));
Accumulator memory computedUserShareDelta = product.position(account).mul(globalShareDelta);
amount = UFixed18Lib.from(programInfo.amountPerShare().mul(computedUserShareDelta).sum());
}
}
There is s a vulnerability in the _unsettled() function, so the problem is in the lineAccumulator memory computedUserShareDelta = product.position(account).mul(globalShareDelta); this line it does not check to see if the product.position(account) function returns a valid number. This means that an attacker could call the _unsettled() function with a negative position, and the function would return a negative amount of rewards. This allow the attacker to steal rewards from the contract.
Impact
This vulnerability allow attackers to steal funds from the contract.
adding the check require(product.position(account) >= 0); to the _unsettled() function, this can help to ensure that the function is effective and that it cannot be bypassed by an attacker.
XDZIBEC
high
XO-
_unsettled()
Function Allows Reward TheftSummary
The
_unsettled()
function does not check to see if theproduct.position(account)
function returns a valid number. This means that an attacker could call the_unsettled()
function with a negative position, this allow the attacker to steal rewards from the contract.Vulnerability Detail
There is s a vulnerability in the _unsettled() function, so the problem is in the line
Accumulator memory computedUserShareDelta = product.position(account).mul(globalShareDelta);
this line it does not check to see if theproduct.position(account)
function returns a valid number. This means that an attacker could call the_unsettled()
function with a negative position, and the function would return a negativeamount
of rewards. This allow the attacker to steal rewards from the contract.Impact
Code Snippet
Tool used
Manual Review
Recommendation
require(product.position(account) >= 0);
to the_unsettled()
function, this can help to ensure that the function is effective and that it cannot be bypassed by an attacker.