Skip to main content
Sigvex

Assembly Analysis

Detects dangerous opcodes, unchecked calls, unsafe memory access, and unguarded arithmetic inside inline assembly blocks.

Assembly Analysis

Overview

Remediation Guide: Assembly Analysis Remediation

Inline assembly bypasses every safety feature Solidity provides: overflow checks, memory bounds, return-value handling, and the free memory pointer discipline. The assembly analysis detector inspects code regions characteristic of hand-written assembly and reports eight distinct finding types, each with its own severity:

Finding Severity
Dangerous opcode (selfdestruct, delegatecall) in assembly Critical
Arbitrary jump destination Critical
Unchecked return value from a low-level call High
Missing success check after an external call High
Direct storage write without validation High
Unsafe memory access (unvalidated bounds) Medium
Return data used without a size check Medium
Unchecked arithmetic (no overflow guard) Medium

The detector’s default severity is high; individual findings carry the severity from the table above.

Why This Is an Issue

Assembly is where compiler protections end. A delegatecall in an assembly block executes foreign code in the caller’s storage context — the pattern behind the 2017 Parity wallet incidents. An arbitrary jump whose destination derives from calldata lets an attacker redirect control flow past access checks. Unchecked arithmetic in assembly silently wraps even on Solidity 0.8+, because checked math does not apply inside assembly { }.

Less severe but still costly: calls whose success flag is dropped fail silently, and returndatacopy without a returndatasize bound can read garbage or revert unexpectedly.

How to Resolve

Re-add the checks the compiler would have inserted, and prefer Solidity over assembly when the gas saving is marginal.

// Before: success flag dropped, no returndata bound
function forward(address target, bytes memory data) external {
    assembly {
        pop(call(gas(), target, 0, add(data, 0x20), mload(data), 0, 0))
    }
}

// After: success checked, returndata bounded
function forward(address target, bytes memory data) external returns (bytes memory out) {
    assembly {
        let ok := call(gas(), target, 0, add(data, 0x20), mload(data), 0, 0)
        if iszero(ok) { revert(0, 0) }
        out := mload(0x40)
        let size := returndatasize()
        mstore(out, size)
        returndatacopy(add(out, 0x20), 0, size)
        mstore(0x40, add(add(out, 0x20), size))
    }
}

Examples

Vulnerable Code

contract Unsafe {
    function drainOnCommand(address target) external {
        assembly {
            // Critical: selfdestruct reachable from an unprotected external function
            selfdestruct(target)
        }
    }
}

Fixed Code

contract Safer {
    address immutable owner = msg.sender;

    function shutdown(address payable target) external {
        require(msg.sender == owner, "not owner");
        // Prefer an explicit sweep over selfdestruct
        target.transfer(address(this).balance);
    }
}

Sample Sigvex Output

{
  "detector_id": "assembly-analysis",
  "severity": "critical",
  "confidence": 0.45,
  "description": "Dangerous opcode in assembly block: DELEGATECALL at offset 0x9e executes with a target that is not access-controlled.",
  "location": { "function": "forward(address,bytes)", "offset": 158 }
}

Detection Methodology

The detector works on decompiled bytecode, classifying instruction patterns characteristic of assembly blocks: raw call sequences whose success flag is never consumed, storage writes with unvalidated keys, jumps whose destination is data-dependent, and arithmetic with no guard branch. Findings are structural pattern matches, so they start at a conservative confidence base. Confidence is then reduced — not removed — when a mitigating context is discovered: access control guarding the assembly path, or a delegatecall inside a recognized proxy/UUPS upgrade function where it is the expected mechanism. Audited library code (OpenZeppelin, Solmate, Solady) is similarly discounted.

Limitations

  • Confidence is pattern-based; assembly that is correct but unusual (custom memory allocators, packed encoders) can be flagged at reduced confidence.
  • Upgrade-flow suppression is keyed to known upgrade function names (upgradeTo, setImplementation, and similar); a proxy with a nonstandard entry point is reported at full confidence.
  • Initializer functions are deliberately not suppressed — a delegatecall inside an initializer is a real risk, not expected behavior.

References