58516 sc low inverted min max logic in alchemistallocator operator cap calculation
Submitted on Nov 2nd 2025 at 23:45:26 UTC by @Tomioka for Audit Comp | Alchemix V3
Report ID: #58516
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistAllocator.sol
Impacts:
Description
Brief/Intro
The allocate function in the AlchemistAllocator contract contains a logic error in the operator cap calculation. When a non admin operator calls this function, the code uses max instead of min when combining the calculated cap with the daoTarget. With the current implementation where daoTarget = type(uint256).max, this gives operators unlimited allocation power, making the operator restriction mechanism non functional.
Vulnerability Details
The AlchemistAllocator manages fund allocations to yield strategies through adapters. It has two permission levels:
Admin: Full control, no restrictions
Operator: Limited control, should have MORE restrictive caps
However, the operator cap logic is inverted:
Allocate Function (allocate):
The Logic Error:
Allocate Operator Cap (Line 39):
Current:
adjusted = adjusted > daoTarget ? adjusted : daoTargetThis is:
adjusted = max(adjusted, daoTarget)Issue: When
daoTarget = type(uint256).max, this gives operators unlimited capsShould be:
adjusted = min(adjusted, daoTarget)to restrict operators
Complete Attack Scenario:
Scenario 1: Operator Bypass on Allocate
Strategy has:
Absolute cap: 1,000 tokens
Relative cap: 2,000 tokens
adjusted = max(1000, 2000) = 2000(line 36)
DAO sets operator limit (daoTarget): 500 tokens
Admin expects operators limited to 500 tokens
Operator calls allocate:
Line 39:
adjusted = max(2000, 500) = 2000Should be:
adjusted = min(2000, 500) = 500
Operator has 2000 token cap instead of 500
Operator bypasses intended 500 token restriction
Scenario 2: Current State with daoTarget = max(uint256)
Currently,
daoTarget = type(uint256).max(line 35)For allocate (line 39):
adjusted = max(adjusted, type(uint256).max)Result:
adjustedalways becomestype(uint256).maxOperators have unlimited cap!
Scenario 3: Future Implementation with Real daoTarget
Code comment says: "FIXME get this from the StrategyClassificationProxy"
When implemented, daoTarget will have real values (e.g., 100, 500, 1000 tokens)
With current inverted logic:
Operators will get max(strategyLimit, operatorLimit) instead of min
High-risk strategies with 10,000 token caps
Operator limit set to 100 tokens (safe limit)
Operator gets 10,000 token access instead of 100
Real-World Impact:
This vulnerability has severe governance and security consequences:
Operator privilege escalation: Operators bypass intended restrictions
Risk management broken: Cannot limit operator exposure
Governance failure: DAO-set limits are inverted
Security degradation: Operators can take excessive risk
Insider risk: Malicious operators have more power than intended
Example Real-World Numbers:
Assume protocol with risk-based operator limits:
Low-risk strategy: Operator limit = 10,000 tokens, Strategy cap = 100,000 tokens
High-risk strategy: Operator limit = 1,000 tokens, Strategy cap = 100,000 tokens
Expected behavior:
Operators can allocate up to 1,000 tokens to high-risk (more restrictive)
Operators can allocate up to 10,000 tokens to low-risk (less restrictive)
Actual behavior with bug:
Operators can allocate up to 100,000 tokens to BOTH (takes max with strategy cap)
Operator limits are completely bypassed
No difference in permissions between admin and operator
Root Cause:
The bug stems from incorrect ternary operator: Using max where min is needed, making operators less restricted than intended.
Comparison with Correct Implementation:
Correct operator cap enforcement:
AlchemistAllocator (broken pattern):
Impact Details
Primary Impact: Operator Privilege Escalation
Operators bypass intended restrictions
Operator limits are inverted (more permissive instead of restrictive)
No functional difference between admin and operator permissions
Governance-set limits are not respected
Secondary Impact: Security and Governance Risks
Risk management broken: Cannot limit operator exposure
Insider threat: Malicious operators have excessive power
Governance failure: DAO votes on limits that don't work
Strategy risk: Operators can over-allocate to risky strategies
No audit trail: Excessive allocations appear "authorized"
Affected Functionality:
The following critical operations have broken permission enforcement:
allocate(line 29) - Operator cap inverted, not restrictiveOperator permission system - Non-functional for allocations
Risk-based strategy limits - Cannot be enforced per role
Affected Users:
Protocol governance: Cannot enforce operator limits
Risk managers: Cannot restrict operator actions
Admin: Must handle all operations (operators untrusted)
All users: Exposed to operator risk-taking
Severity Justification:
Current state: Operators have unlimited allocation power
Future severity: When implemented correctly, becomes critical privilege escalation
Governance violation: Set limits will have opposite effect
Silent failure: Code appears to enforce limits but doesn't
References
Vulnerable allocate function: src/AlchemistAllocator.sol:29-44
Inverted operator cap: src/AlchemistAllocator.sol:39
Proof of Concept
Proof of Concept
To run the PoC for Vulnerability, execute this command: forge test --match-path test/AllocatorInvertedLogic.t.sol -vvv
Recommended Mitigation
Fix the inverted logic:
Was this helpful?