Unsafe Downcasting
Detects type casts that silently truncate values when converting from a larger to a smaller integer type.
Unsafe Downcasting
Overview
The unsafe downcasting detector identifies integer type conversions where a value is cast from a larger type (e.g., uint256) to a smaller type (e.g., uint128, uint96, uint64) without verifying that the value fits within the smaller type’s range. Solidity does not revert on explicit downcasts prior to 0.8.0, and even in 0.8.0+ the behavior depends on whether unchecked blocks are used. Silent truncation can corrupt balances, timestamps, or other critical values.
Why This Is an Issue
A uint256 value of 2^128 + 1 cast to uint128 silently becomes 1. If this occurs with a token balance, a user’s holdings are truncated to a fraction of their actual value. If it occurs with a timestamp, deadline checks become meaningless. The Cetus Protocol exploit (June 2025, $223M) involved a related issue where unchecked bit operations caused value truncation in a concentrated liquidity pool.
How to Resolve
// Before: Vulnerable — silent truncation
function packBalance(uint256 balance) internal pure returns (uint128) {
return uint128(balance); // Truncates if balance > 2^128
}
// After: Fixed — checked downcast
function packBalance(uint256 balance) internal pure returns (uint128) {
require(balance <= type(uint128).max, "Balance overflow");
return uint128(balance);
}
// Or use OpenZeppelin SafeCast
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
function packBalance(uint256 balance) internal pure returns (uint128) {
return SafeCast.toUint128(balance); // Reverts on overflow
}
Detection Methodology
- Cast instruction identification: Detects AND masking operations (e.g.,
AND 0xFFFFFFFFFFFFFFFFfor uint64 truncation) and explicit type narrowing in the IR. - Source type inference: Infers the source value’s bit width from its origin (storage slot type, function parameter type, arithmetic result).
- Bounds check absence: Flags casts where no preceding comparison limits the value to the target type’s range.
- Context sensitivity: Casts within
uncheckedblocks or in Solidity < 0.8 receive higher severity.
Limitations
False positives: Casts where the source value is provably bounded (e.g., a counter that never exceeds 1000) may be flagged because the bound is enforced by external logic rather than an explicit check. False negatives: Custom packing schemes using shift-and-mask arithmetic may not be recognized as downcasts.
Related Detectors
- Integer Overflow — detects arithmetic overflow broadly
- Bit Shift Overflow — detects unchecked shift amounts
- Precision Errors — detects precision loss in calculations