Exemplo n.º 1
0
def test_concrete_call_symbolic_to():
    # arrange
    address = "0x10"

    active_account = Account(address)
    active_account.code = Disassembly("00")
    environment = Environment(active_account, None, None, None, None, None)
    state = GlobalState(None, environment, None)
    state.mstate.memory = ["placeholder", "calldata_bling_0"]

    node = Node("example")
    node.contract_name = "the contract name"
    node.function_name = "the function name"

    to = Variable("calldata_3", VarType.SYMBOLIC)
    meminstart = Variable(1, VarType.CONCRETE)
    call = Call(node, state, None, None, to, None)

    # act
    issues = _concrete_call(call, state, address, meminstart)

    # assert
    issue = issues[0]
    assert issue.address == address
    assert issue.contract == node.contract_name
    assert issue.function == node.function_name
    assert issue.title == "Call data forwarded with delegatecall()"
    assert issue.type == "Informational"
    assert (
        issue.description
        == "This contract forwards its call data via DELEGATECALL in its fallback function."
        " This means that any function in the called contract can be executed."
        " Note that the callee contract will have access to the storage of the "
        "calling contract.\n DELEGATECALL target: calldata_3"
    )
Exemplo n.º 2
0
def test_delegate_call(sym_mock, concrete_mock, curr_instruction):
    # arrange
    # sym_mock = mocker.patch.object(delegatecall, "_symbolic_call")
    # concrete_mock = mocker.patch.object(delegatecall, "_concrete_call")
    sym_mock.return_value = []
    concrete_mock.return_value = []
    curr_instruction.return_value = {"address": "0x10"}

    active_account = Account("0x10")
    active_account.code = Disassembly("00")

    environment = Environment(active_account, None, None, None, None, None)
    state = GlobalState(None, environment, Node)
    state.mstate.memory = ["placeholder", "calldata_bling_0"]
    state.mstate.stack = [1, 2, 3]
    assert state.get_current_instruction() == {"address": "0x10"}

    node = Node("example")
    node.contract_name = "the contract name"
    node.function_name = "fallback"

    to = Variable("storage_1", VarType.SYMBOLIC)
    call = Call(node, state, None, "DELEGATECALL", to, None)

    statespace = MagicMock()
    statespace.calls = [call]

    # act
    execute(statespace)

    # assert
    assert concrete_mock.call_count == 1
    assert sym_mock.call_count == 1
Exemplo n.º 3
0
def test_symbolic_call_storage_to(mocker):
    # arrange
    address = "0x10"

    active_account = Account(address)
    active_account.code = Disassembly("00")
    environment = Environment(active_account, None, None, None, None, None)
    state = GlobalState(None, environment, None)
    state.mstate.memory = ["placeholder", "calldata_bling_0"]

    node = Node("example")
    node.contract_name = "the contract name"
    node.function_name = "the function name"

    to = Variable("storage_1", VarType.SYMBOLIC)
    call = Call(node, state, None, "Type: ", to, None)

    mocker.patch.object(SymExecWrapper, "__init__", lambda x, y: None)
    statespace = SymExecWrapper(1)

    mocker.patch.object(statespace, "find_storage_write")
    statespace.find_storage_write.return_value = "Function name"

    # act
    issues = _symbolic_call(call, state, address, statespace)

    # assert
    issue = issues[0]
    assert issue.address == address
    assert issue.contract == node.contract_name
    assert issue.function == node.function_name
    assert issue.title == "Type:  to a user-supplied address"
    assert issue.type == "Informational"
    assert (
        issue.description
        == "This contract delegates execution to a contract address in storage slot 1."
        " This storage slot can be written to by calling the function `Function name`. "
        "Be aware that the called contract gets unrestricted access to this contract's state."
    )
Exemplo n.º 4
0
def test_vmtest(test_name: str, pre_condition: dict, action: dict,
                post_condition: dict) -> None:
    # Arrange
    if test_name == "gasprice":
        return
    accounts = {}
    for address, details in pre_condition.items():
        account = Account(address)
        account.code = Disassembly(details["code"][2:])
        account.balance = int(details["balance"], 16)
        account.nonce = int(details["nonce"], 16)

        accounts[address] = account

    laser_evm = LaserEVM(accounts)

    # Act
    laser_evm.time = datetime.now()

    # TODO: move this line below and check for VmExceptions when gas has been implemented
    if post_condition == {}:
        return

    execute_message_call(
        laser_evm,
        callee_address=action["address"],
        caller_address=action["caller"],
        origin_address=binascii.a2b_hex(action["origin"][2:]),
        code=action["code"][2:],
        gas=action["gas"],
        data=binascii.a2b_hex(action["data"][2:]),
        gas_price=int(action["gasPrice"], 16),
        value=int(action["value"], 16),
    )

    # Assert

    assert len(laser_evm.open_states) == 1

    world_state = laser_evm.open_states[0]
    model = get_model(
        next(iter(laser_evm.nodes.values())).states[0].mstate.constraints)

    for address, details in post_condition.items():
        account = world_state[address]

        assert account.nonce == int(details["nonce"], 16)
        assert account.code.bytecode == details["code"][2:]

        for index, value in details["storage"].items():
            expected = int(value, 16)
            actual = account.storage[int(index, 16)]
            if isinstance(actual, ExprRef):
                actual = model.eval(actual)
                actual = (
                    1 if actual == True else 0 if actual == False else actual
                )  # Comparisons should be done with == than 'is' here as actual can be a BoolRef
            else:
                if type(actual) == bytes:
                    actual = int(binascii.b2a_hex(actual), 16)
                elif type(actual) == str:
                    actual = int(actual, 16)
            assert actual == expected
Exemplo n.º 5
0
def test_vmtest(
    test_name: str,
    environment: dict,
    pre_condition: dict,
    action: dict,
    gas_used: int,
    post_condition: dict,
) -> None:
    # Arrange
    if test_name == "gasprice":
        return
    accounts = {}
    for address, details in pre_condition.items():
        account = Account(address)
        account.code = Disassembly(details["code"][2:])
        account.balance = int(details["balance"], 16)
        account.nonce = int(details["nonce"], 16)

        accounts[address] = account

    laser_evm = LaserEVM(accounts)

    # Act
    laser_evm.time = datetime.now()

    final_states = execute_message_call(
        laser_evm,
        callee_address=action["address"],
        caller_address=action["caller"],
        origin_address=binascii.a2b_hex(action["origin"][2:]),
        code=action["code"][2:],
        gas_limit=int(action["gas"], 16),
        data=binascii.a2b_hex(action["data"][2:]),
        gas_price=int(action["gasPrice"], 16),
        value=int(action["value"], 16),
        track_gas=True,
    )

    # Assert
    if gas_used is not None and gas_used < int(environment["currentGasLimit"],
                                               16):
        # avoid gas usage larger than block gas limit
        # this currently exceeds our estimations
        gas_min_max = [(s.mstate.min_gas_used, s.mstate.max_gas_used)
                       for s in final_states]
        gas_ranges = [g[0] <= gas_used for g in gas_min_max]
        assert all(map(lambda g: g[0] <= g[1], gas_min_max))
        assert any(gas_ranges)

    if any(
        (v in test_name for v in ["error", "oog"])) and post_condition == {}:
        # no more work to do if error happens or out of gas
        assert len(laser_evm.open_states) == 0
    else:
        assert len(laser_evm.open_states) == 1
        world_state = laser_evm.open_states[0]
        model = get_model(
            next(iter(laser_evm.nodes.values())).states[0].mstate.constraints,
            enforce_execution_time=False,
        )

        for address, details in post_condition.items():
            account = world_state[address]

            assert account.nonce == int(details["nonce"], 16)
            assert account.code.bytecode == details["code"][2:]

            for index, value in details["storage"].items():
                expected = int(value, 16)
                actual = account.storage[int(index, 16)]
                if isinstance(actual, Expression):
                    actual = actual.value
                    actual = 1 if actual is True else 0 if actual is False else actual
                else:
                    if type(actual) == bytes:
                        actual = int(binascii.b2a_hex(actual), 16)
                    elif type(actual) == str:
                        actual = int(actual, 16)
                assert actual == expected
Exemplo n.º 6
0
def test_vmtest(
    test_name: str,
    environment: dict,
    pre_condition: dict,
    action: dict,
    gas_used: int,
    post_condition: dict,
) -> None:

    # Arrange
    if test_name in ignored_test_names:
        return

    world_state = WorldState()

    for address, details in pre_condition.items():
        account = Account(address, concrete_storage=True)
        account.code = Disassembly(details["code"][2:])
        account.nonce = int(details["nonce"], 16)
        for key, value in details["storage"].items():
            account.storage[int(key, 16)] = int(value, 16)

        world_state.put_account(account)
        account.set_balance(int(details["balance"], 16))

    laser_evm = LaserEVM()
    laser_evm.open_states = [world_state]

    # Act
    laser_evm.time = datetime.now()

    final_states = execute_message_call(
        laser_evm,
        callee_address=symbol_factory.BitVecVal(int(action["address"], 16),
                                                256),
        caller_address=symbol_factory.BitVecVal(int(action["caller"], 16),
                                                256),
        origin_address=symbol_factory.BitVecVal(int(action["origin"], 16),
                                                256),
        code=action["code"][2:],
        gas_limit=int(action["gas"], 16),
        data=binascii.a2b_hex(action["data"][2:]),
        gas_price=int(action["gasPrice"], 16),
        value=int(action["value"], 16),
        track_gas=True,
    )

    # Assert
    if gas_used is not None and gas_used < int(environment["currentGasLimit"],
                                               16):
        # avoid gas usage larger than block gas limit
        # this currently exceeds our estimations
        gas_min_max = [(s.mstate.min_gas_used, s.mstate.max_gas_used)
                       for s in final_states]
        gas_ranges = [g[0] <= gas_used for g in gas_min_max]
        assert all(map(lambda g: g[0] <= g[1], gas_min_max))
        assert any(gas_ranges)

    if post_condition == {}:
        # no more work to do if error happens or out of gas
        assert len(laser_evm.open_states) == 0
    else:
        assert len(laser_evm.open_states) == 1
        world_state = laser_evm.open_states[0]

        for address, details in post_condition.items():
            account = world_state[symbol_factory.BitVecVal(
                int(address, 16), 256)]

            assert account.nonce == int(details["nonce"], 16)
            assert account.code.bytecode == details["code"][2:]

            for index, value in details["storage"].items():
                expected = int(value, 16)
                actual = account.storage[int(index, 16)]

                if isinstance(actual, Expression):
                    actual = actual.value
                    actual = 1 if actual is True else 0 if actual is False else actual
                else:
                    if type(actual) == bytes:
                        actual = int(binascii.b2a_hex(actual), 16)
                    elif type(actual) == str:
                        actual = int(actual, 16)
                assert actual == expected