Skip to main content
Sigvex

Unchecked Array Bounds

Detects array accesses without bounds validation, enabling out-of-bounds reads/writes that corrupt storage or cause unexpected reverts.

Unchecked Array Bounds

Overview

The unchecked array bounds detector identifies storage and memory array accesses where the index is derived from user input or external data without a preceding bounds check. While Solidity 0.8+ reverts on out-of-bounds access to storage arrays, dynamic memory arrays and assembly-level operations do not have this protection. Additionally, even with automatic bounds checks, relying on revert-based protection can cause denial of service.

Why This Is an Issue

Array bounds violations cause two distinct problems:

  1. Denial of service: If an attacker can trigger an out-of-bounds access, the transaction reverts, blocking the entire operation. In batch operations or governance execution, this can stall critical functions.
  2. Storage corruption (assembly): In assembly blocks or contracts compiled with older Solidity versions, out-of-bounds writes can corrupt adjacent storage slots, overwriting critical variables like ownership or balances.

DEX and vault contracts that use indices to reference pools or positions are particularly vulnerable. An attacker providing an invalid pool index can DoS withdrawal functions or, in older contracts, corrupt pool accounting.

How to Resolve

// Before: Index from calldata used without validation
function getPool(uint256 index) external view returns (Pool memory) {
    return pools[index]; // Reverts if index >= pools.length, causing DoS
}

// After: Explicit bounds check with informative error
function getPool(uint256 index) external view returns (Pool memory) {
    require(index < pools.length, "Invalid pool index");
    return pools[index];
}

Examples

Vulnerable Code

contract VulnerableRegistry {
    address[] public validators;

    // Attacker can pass any index -- no validation
    function removeValidator(uint256 index) external onlyAdmin {
        // If index >= validators.length, reverts with panic
        // In batch removal, one bad index blocks the entire batch
        validators[index] = validators[validators.length - 1];
        validators.pop();
    }
}

Fixed Code

contract SafeRegistry {
    address[] public validators;

    function removeValidator(uint256 index) external onlyAdmin {
        require(index < validators.length, "Index out of bounds");
        validators[index] = validators[validators.length - 1];
        validators.pop();
    }
}

Sample Sigvex Output

{
  "detector_id": "unchecked-array-bounds",
  "severity": "high",
  "confidence": 0.85,
  "description": "Array access at offset 0x8e uses user-supplied index parameter without bounds validation. An out-of-range index causes a panic revert, enabling DoS of the removeValidator() function.",
  "location": { "function": "removeValidator(uint256)", "offset": 142 }
}

Detection Methodology

  1. Index source tracking: Traces array index values back to their origin (calldata parameters, storage reads, external call returns).
  2. Bounds check detection: Scans for comparison operations (LT, GT) against array length before the array access.
  3. Assembly analysis: Checks mload/mstore/sload/sstore in assembly blocks for unchecked offset calculations.
  4. Confidence scoring: Higher confidence when the index originates directly from calldata; lower when derived from trusted internal state.

Limitations

  • Solidity 0.8+ automatic bounds checking on storage arrays means the vulnerability manifests as DoS rather than corruption. The detector flags both.
  • Cannot determine whether a revert-on-out-of-bounds is intentional design (e.g., the function is expected to revert for invalid inputs).
  • Mapping accesses (which have no bounds concept) are excluded.

References