Sign Extension Overflow Remediation
Overview
Casting a negative signed integer to an unsigned type produces a value near the unsigned maximum: uint256(int8(-1)) becomes 2^256 - 1. The EVM’s SIGNEXTEND fills the high bits with ones, so a small negative number turns into an enormous credit, price, or length. The fix is to check the sign before converting and handle the negative branch explicitly.
Related Detector: Sign Extension Overflow
Recommended Fix
Before (Vulnerable)
function applyDelta(int256 delta) external {
uint256 amount = uint256(delta); // delta < 0 -> amount near 2^256
balances[msg.sender] += amount;
}
After (Fixed)
function applyDelta(int256 delta) external {
require(delta >= 0, "Negative delta");
uint256 amount = uint256(delta);
balances[msg.sender] += amount;
}
For logic that must accept negative inputs, branch on the sign and operate in the correct domain rather than reinterpreting the bit pattern:
function updatePrice(int256 priceChange) external {
if (priceChange >= 0) {
currentPrice = basePrice + uint256(priceChange);
} else {
uint256 decrease = uint256(-priceChange);
require(basePrice >= decrease, "Price underflow");
currentPrice = basePrice - decrease;
}
}
Guarding the cast guarantees the unsigned value is the true magnitude, not a sign-extended wrap.
Alternative Mitigations
- Use a signed-math library with explicit
toUint256/toInt256conversions that revert on out-of-range or negative inputs, so every conversion is checked in one audited place. - Keep arithmetic in the signed domain end-to-end and convert only at the boundary where a non-negative result is provable.
- Where assembly performs
SIGNEXTENDmanually, add the same sign guard before the conversion.
Common Mistakes
- Relying on Solidity 0.8 overflow checks to catch this. Those checks cover arithmetic, not the explicit
uint256(signedValue)cast, which still wraps silently. - Comparing the converted unsigned value against a bound after the cast — by then the negative value has already become a large positive that may pass the check.
- Negating
int256minimum (type(int256).min) before converting, which itself overflows.