Pendle Aura Balancer and other markets have the potential to disrupt all functions within PendlePowerFarmToken that depend on the syncSupply() modifier. This is because certain contracts within the protocol do not account for the possibility of new reward tokens being introduced in existing markets.
Proof of Concept
PendlePowerFarmController::addPendleMarket() currently initializes pendleChildCompoundInfo[_pendleMarket].lastIndex as a dynamic fixed-length array based on the length returned from IPendleMarket(_pendleMarket).getRewardTokens() at the time of the initialization.
This will work fine but has a risk of breaking because Pendle Aura Balancer and some other markets can have new reward tokens introduced at any time. Here's, for example, PendleAuraBalancerStableLPSYV3Upg's source:
function addRewardTokens(address token) external virtual onlyOwner {
extraRewards.push(token);
}
function extraRewardsLength() external view virtual returns (uint256) {
return extraRewards.length;
}
function _getRewardTokens() internal view virtual override returns (address[] memory res) {
uint256 extraRewardsLen = extraRewards.length;
res = new address[](2 + extraRewardsLen);
res[0] = BAL_TOKEN;
res[1] = AURA_TOKEN;
for (uint256 i = 0; i < extraRewardsLen; i++) {
res[2 + i] = extraRewards[i];
}
}
All functions using the syncSupply modifier inside PendlePowerFarmToken will get DOSed when _overWriteCheck() is called:
As we can see overWriteIndexAll() is focused on aligning pendleChildCompoundInfo[_pendleMarket].lastIndex's values with the ones from the Pendle market. As seen in PendlePowerFarmController::addPendleMarket(), the last index array was initialized with a fixed length, so any new index overWriteIndexAll() tries to insert at will result in a revert unless added through a push().
Tools Used
Manual Review
Recommended Mitigation Steps
Refactor PendlePowerFarmController::overWriteIndex() so it uses push() for the newly added reward tokens.
Lines of code
https://github.com/pendle-finance/pendle-core-v2-public/blob/main/contracts/core/StandardizedYield/implementations/BalancerStable/base/PendleAuraBalancerStableLPSYV3Upg.sol#L273-L279 https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmController.sol#L211-L291 https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmToken.sol#L81-L96 https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmToken.sol#L149-L194 https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmController.sol#L293-L319 https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmController.sol#L526-L564 https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmControllerHelper.sol#L236-L243
Vulnerability details
Impact
Pendle Aura Balancer and other markets have the potential to disrupt all functions within
PendlePowerFarmToken
that depend on thesyncSupply()
modifier. This is because certain contracts within the protocol do not account for the possibility of new reward tokens being introduced in existing markets.Proof of Concept
PendlePowerFarmController::addPendleMarket()
currently initializespendleChildCompoundInfo[_pendleMarket].lastIndex
as a dynamic fixed-length array based on the length returned fromIPendleMarket(_pendleMarket).getRewardTokens()
at the time of the initialization.This will work fine but has a risk of breaking because Pendle Aura Balancer and some other markets can have new reward tokens introduced at any time. Here's, for example,
PendleAuraBalancerStableLPSYV3Upg
's source:All functions using the
syncSupply
modifier insidePendlePowerFarmToken
will get DOSed when_overWriteCheck()
is called:The pieces we're primarily interested in here are
PENDLE_CONTROLLER.updateRewardTokens()
andPENDLE_CONTROLLER.overWriteIndex()
:_setRewardTokens()
comes from the helper contractPendlePowerFarmControllerHelper
that the controller inherits:As we can see
overWriteIndexAll()
is focused on aligningpendleChildCompoundInfo[_pendleMarket].lastIndex
's values with the ones from the Pendle market. As seen inPendlePowerFarmController::addPendleMarket()
, the last index array was initialized with a fixed length, so any new indexoverWriteIndexAll()
tries to insert at will result in a revert unless added through apush()
.Tools Used
Manual Review
Recommended Mitigation Steps
Refactor
PendlePowerFarmController::overWriteIndex()
so it usespush()
for the newly added reward tokens.Assessed type
DoS