Skip to main content
Sigvex

Arbitrary Jump

Detects JUMP or JUMPI instructions where the destination is derived from user input, enabling control-flow hijacking.

Arbitrary Jump

Overview

Remediation Guide: How to Fix Arbitrary Jump

The arbitrary jump detector identifies JUMP or JUMPI instructions where the destination address on the stack is derived from user-controlled input. In the EVM, JUMP transfers execution to a JUMPDEST instruction at the specified offset. If an attacker controls this offset, they can redirect execution to any JUMPDEST in the contract, bypassing access control checks, re-entering critical sections, or reaching code paths that were not intended to be directly accessible.

Sigvex traces the jump destination operand through the data-flow graph to determine whether it originates from calldata.

Why This Is an Issue

While the EVM restricts jump targets to valid JUMPDEST instructions (not arbitrary bytecode offsets), a contract with multiple JUMPDEST markers still provides an attacker with several potential landing points. An attacker who controls the jump destination can skip over require checks, bypass modifier logic, or jump directly to fund-transfer code that was meant to be guarded.

This vulnerability is less common in modern Solidity (which generates computed jumps only for internal dispatch) but can appear in hand-written assembly, Yul contracts, or contracts generated by non-standard compilers.

How to Resolve

// Before: Vulnerable — user-controlled jump destination
function dispatch(uint256 dest) external {
    assembly {
        jump(dest)  // Attacker chooses execution target
    }
}

// After: Fixed — use a switch statement with known destinations
function dispatch(uint256 action) external {
    if (action == 1) { _handleDeposit(); }
    else if (action == 2) { _handleWithdraw(); }
    else { revert("Unknown action"); }
}

Examples

Vulnerable Code

contract VulnerableDispatch {
    // User-controlled jump target
    function execute(uint256 jumpTarget) external payable {
        assembly {
            jump(jumpTarget)  // Control-flow hijacking
        }
    }
}

Fixed Code

contract SafeDispatch {
    function execute(uint256 action) external payable {
        if (action == 0) {
            _deposit();
        } else if (action == 1) {
            _withdraw();
        } else {
            revert("Invalid action");
        }
    }

    function _deposit() internal { /* ... */ }
    function _withdraw() internal { /* ... */ }
}

Sample Sigvex Output

{
  "detector_id": "arbitrary-jump",
  "severity": "critical",
  "confidence": 0.85,
  "description": "JUMP at offset 0x3f uses a destination from calldata. An attacker can redirect execution to any JUMPDEST in the contract, bypassing access control.",
  "location": { "function": "execute(uint256)", "offset": 63 }
}

Detection Methodology

  1. JUMP/JUMPI identification: Locates all JUMP and JUMPI instructions in the bytecode.
  2. Destination taint analysis: Traces the destination operand backward to determine if it originates from user input (CALLDATALOAD).
  3. Compiler pattern exclusion: Excludes the standard Solidity function selector dispatch pattern (which uses computed jumps from CALLDATALOAD but only to compiler-generated JUMPDEST locations in the dispatcher).
  4. JUMPDEST enumeration: Counts available JUMPDEST markers to assess the attack surface — more destinations mean higher risk.

Limitations

False positives:

  • Solidity’s internal function dispatch mechanism uses computed jumps from calldata, but these are restricted to the function selector table. The detector excludes this pattern, but edge cases may trigger false positives.

False negatives:

  • Jump destinations computed through complex arithmetic may not be traced to user input.
  • Contracts using the JUMPI conditional variant with a user-controlled condition (but compiler-controlled destination) are a different vulnerability class.

References