Assessment reports>Concrete>Medium findings>The ,protectStrategy, state variable is not updated during ,emergencyRemoveStrategy, call
Category: Coding Mistakes

The protectStrategy state variable is not updated during emergencyRemoveStrategy call

Medium Impact
High Severity
Low Likelihood

Description

The emergencyRemoveStrategy function allows the owner of ConcreteMultiStrategyVault to remove a strategy in emergency situations.

function emergencyRemoveStrategy(uint256 index_, bool forceEject_) external onlyOwner {
    StrategyHelper.emergencyRemoveStrategy(strategies, asset(), index_, forceEject_, protectStrategy);
}

The StrategyHelper.emergencyRemoveStrategy function attempts to clear protectStrategy if the strategy being removed is the designated protected strategy. However, the protectStrategy function argument is passed to the library function as a memory copy, not a storage reference. As a result, any update to protectStrategy inside the library function does not affect the protectStrategy state variable in the ConcreteMultiStrategyVault contract.

function emergencyRemoveStrategy(
    Strategy[] storage strategies,
    address asset,
    uint256 index_,
    bool forceEject_,
    address protectStrategy
) external {
    // [...]
    if (forceEject_) {
        // [...]
        if (address(stratToRemove.strategy) == protectStrategy) {
            protectStrategy = address(0);
        }
        // [...]
    } else {
        // Normal removal process
        removeStrategy(stratToRemove.strategy, protectStrategy, IERC20(asset));
    }
}

function removeStrategy(IStrategy stratToBeRemoved_, address protectStrategy_, IERC20 asset)
    public
    returns (address protectStrategy)
{
    // [...]
    if (protectStrategy_ == address(stratToBeRemoved_)) {
        protectStrategy = address(0);
    } else {
        // [...]
    }
    // [...]
}

Consequently, after the owner calls emergencyRemoveStrategy to remove the protected strategy, the protectStrategy state variable remains unchanged.

Impact

When the owner removes the current protectStrategy using emergencyRemoveStrategy, the strategy's address remains in the protectStrategy state variable. The removed strategy retains its privileged permissions and can still call functions protected by the onlyProtect modifier, such as the function requestFunds, which could request funds from the vault.

Recommendations

Modify StrategyHelper.emergencyRemoveStrategy to return the updated protected strategy address. Update ConcreteMultiStrategyVault.emergencyRemoveStrategy to capture this return value, and update the protectStrategy state variable accordingly.

Remediation

This issue has been acknowledged by Blueprint Finance, and a fix was implemented in commit 8b5c7fd0.

Zellic © 2025Back to top ↑