Skip to content

🐞 Incorrect Sanitizer Ordering for Base58 decodeWord #1543

Description

@roger-bai-coinbase

Description

The Base58.decodeWord() function has a sanitizer ordering bug: it performs mload(c) before validating that c is within the lookup table bounds. When a byte with ASCII value < 0x31 (e.g. 0) is passed, c underflows to ~2^256, causing EVM memory expansion that exceeds block gas limits and triggers OutOfGas instead of the expected clean revert.

PoC

    function testDecodeWordSanitizerOrderingBug() public {
        uint256 g = gasleft();
        vm.expectRevert(Base58.Base58DecodingError.selector);
        this.decodeBytes("0");
        uint256 decodeGas = g - gasleft();
        assertLt(decodeGas, 7_000, "decode('0') should revert cleanly using less than 7000 gas");

        vm.expectRevert(); // InvalidOperandOOG — no revert data. See -vvvv trace.
        this.decodeWord("0");

        g = gasleft();
        (bool success, bytes memory reason) =
            address(this).call(abi.encodeWithSelector(this.decodeWord.selector, "0"));
        uint256 decodeWordGas = g - gasleft();
        assertFalse(success, "decodeWord('0') should revert with InvalidOperandOOG");
        assertEq(reason.length, 0, "decodeWord('0') should revert without return data");
        assertGt(decodeWordGas, 90_000_000, "decodeWord('0') should use more than 90M gas");
    }

    function decodeBytes(string memory encoded) public pure returns (bytes memory) {
        return Base58.decode(encoded);
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions