Skip to main content
Sigvex

Cross-Contract Taint Remediation

How to stop untrusted external-call return values from steering access control, transfers, or storage keys.

Cross-Contract Taint Remediation

Overview

Related Detector: Cross-Contract Taint

When the return value of an untrusted external call flows into a sensitive sink — a delegatecall target, a transfer amount, an access-control decision, a storage key — the callee controls your contract’s behaviour. The remediation is to treat every external return value as untrusted input and validate it against your own state before it reaches a sink.

Before (Vulnerable)

function settle(address quoter) external {
    // The quoted amount comes from an untrusted contract and is paid out directly.
    uint256 amount = IQuoter(quoter).quote();
    payable(msg.sender).transfer(amount);
}

After (Fixed — validate against trusted state and bounds)

function settle(address quoter) external {
    require(trustedQuoter[quoter], "Untrusted quoter");     // constrain the source
    uint256 amount = IQuoter(quoter).quote();
    require(amount <= owed[msg.sender], "Exceeds entitlement");  // bound by own accounting
    owed[msg.sender] -= amount;                              // effect before interaction
    (bool ok, ) = msg.sender.call{value: amount}("");
    require(ok, "Transfer failed");
}

Sensitive sinks ranked by risk: a tainted delegatecall target is critical, tainted call targets and values are high. Never let any of them derive from an unvalidated external return.

Common Mistakes

  • Trusting a return value because the interface is known, ignoring that the implementation is attacker-controlled.
  • Validating the amount but letting a tainted address reach a call/delegatecall target.
  • Using an external contract’s returned index or id directly as a storage key.

References