Remediating Arbitrary External Calls
How to prevent external calls to user-controlled addresses by validating targets against whitelists.
Remediating Arbitrary External Calls
Overview
Related Detector: Arbitrary External Calls
External calls to user-supplied addresses must be validated before execution. The recommended approach depends on whether the set of valid targets is known at deployment or changes over time.
Recommended Fix
Before (Vulnerable)
function relay(address target, bytes calldata data) external {
(bool success, ) = target.call(data);
require(success);
}
After (Fixed)
address public immutable trustedTarget;
constructor(address _target) {
trustedTarget = _target;
}
function relay(bytes calldata data) external {
(bool success, ) = trustedTarget.call(data);
require(success);
}
Alternative Mitigations
- Whitelist mapping: For multiple valid targets, use
mapping(address => bool) public approvedwith admin-only setter. - Interface validation: Call
ERC165.supportsInterface()on the target before executing. This does not prevent malicious contracts but confirms the target implements the expected interface. - Immutable addresses: Use
immutablefor addresses that should not change after deployment.
Common Mistakes
- Checking code size only:
require(target.code.length > 0)prevents calls to EOAs but does not prevent calls to malicious contracts. - Post-call validation: Checking return data after the call does not prevent the external code from executing side effects during the call.