Skip to content
This repository has been archived by the owner on Sep 5, 2023. It is now read-only.

Commit

Permalink
test failure on invalid calldata
Browse files Browse the repository at this point in the history
  • Loading branch information
temyurchenko committed Dec 10, 2021
1 parent eae75f3 commit 5ee768b
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 3 deletions.
24 changes: 24 additions & 0 deletions tests/behaviour/friendly.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
pragma solidity ^0.8.6;

interface IFriend {
function call_friend(IFriend friend) external view returns (uint);

function tell_number(IFriend friend) external view returns (uint);
}

contract Friend is IFriend {
uint _number;

constructor(uint number) {
_number = number;
}

function call_friend(IFriend friend) external view returns (uint) {
return friend.tell_number(this);
}

function tell_number(IFriend friend) external view returns (uint) {
require(msg.sender == address(friend));
return _number;
}
}
12 changes: 12 additions & 0 deletions tests/behaviour/hacker.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
%lang starknet
%builtins range_check

from starkware.cairo.common.alloc import alloc

@external
func __main(calldata_size : felt, calldata_len : felt, calldata : felt*) -> (
returndata_size : felt, returndata_len : felt, returndata : felt*):
let (returndata) = alloc()
assert returndata[0] = 2 ** 128
return (16, 1, returndata)
end
94 changes: 94 additions & 0 deletions tests/behaviour/malicious-calldata_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import asyncio
import tempfile
from pathlib import Path

import pytest
from cli.encoding import get_cairo_calldata, get_ctor_evm_calldata, get_evm_calldata
from starkware.starknet.compiler.compile import (
compile_starknet_codes,
compile_starknet_files,
)
from starkware.starknet.testing.state import StarknetState
from starkware.starkware_utils.error_handling import StarkException
from yul.main import transpile_from_solidity
from yul.starknet_utils import deploy_contract, invoke_method

WARP_ROOT = Path(__file__).parents[2]
CAIRO_PATH = WARP_ROOT / "warp" / "cairo-src"
TEST_DIR = Path(__file__).parent


@pytest.fixture
async def starknet():
return await StarknetState.empty()


@pytest.fixture
def friendly_info():
friendly_file = TEST_DIR / "friendly.sol"
return transpile_from_solidity(friendly_file, "Friend")


@pytest.fixture
def friendly_def(friendly_info):
with tempfile.NamedTemporaryFile(mode="w", suffix=".cairo") as tmp:
tmp.write(friendly_info["cairo_code"])
yield compile_starknet_codes(
[(friendly_info["cairo_code"], tmp.name)],
debug_info=True,
cairo_path=[CAIRO_PATH],
)


@pytest.fixture
async def friendly_address(starknet, friendly_info, friendly_def):
return await deploy_contract(starknet, friendly_info, friendly_def, 8)


@pytest.mark.asyncio
async def test_normal_contract_call(
starknet, friendly_info, friendly_def, friendly_address
):
friendly_address2 = await deploy_contract(starknet, friendly_info, friendly_def, 13)
result = await invoke_method(
starknet, friendly_info, friendly_address, "call_friend", friendly_address2
)
assert result.retdata == [32, 2, 0, 13]


@pytest.mark.asyncio
async def test_malicious_deploy(starknet, friendly_info, friendly_def):
evm_calldata = get_ctor_evm_calldata(friendly_info["sol_abi"], [0])
cairo_calldata = get_cairo_calldata(evm_calldata)
cairo_calldata[-1] = 2 ** 128 # tamper with calldata to make it invalid
exc_msg = rf"Value {2**128}, in range check builtin \d*, is out of range \[0, {2**128}\)\."
with pytest.raises(StarkException, match=exc_msg):
await starknet.deploy(friendly_def, cairo_calldata)


@pytest.mark.asyncio
async def test_malicious_direct_call(starknet, friendly_info, friendly_address):
evm_calldata = get_evm_calldata(friendly_info["sol_abi"], "call_friend", [0])
cairo_calldata = get_cairo_calldata(evm_calldata)
cairo_calldata[-1] = 2 ** 128 # tamper with calldata to make it invalid
exc_msg = rf"Value {2**128}, in range check builtin \d*, is out of range \[0, {2**128}\)\."
with pytest.raises(StarkException, match=exc_msg):
await starknet.invoke_raw(
contract_address=friendly_address,
selector="__main",
calldata=cairo_calldata,
caller_address=0,
)


@pytest.mark.asyncio
async def test_malicious_contract_call(starknet, friendly_info, friendly_address):
hacker_file = TEST_DIR / "hacker.cairo"
hacker_def = compile_starknet_files([str(hacker_file)], debug_info=True)
hacker_address = await starknet.deploy(hacker_def, [])

exc_msg = rf"Value {2**128}, in range check builtin \d*, is out of range \[0, {2**128}\)\."
with pytest.raises(StarkException, match=exc_msg):
await invoke_method(
starknet, friendly_info, friendly_address, "call_friend", hacker_address
)
7 changes: 5 additions & 2 deletions warp/yul/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@


def transpile_from_solidity(sol_src_path, main_contract) -> dict:
sol_src_path_modified = sol_src_path[:-4] + "_marked.sol"
sol_src_path_modified = str(sol_src_path)[:-4] + "_marked.sol"
if not shutil.which(AST_GENERATOR):
sys.exit(f"Please install {AST_GENERATOR} first")

Expand All @@ -53,7 +53,10 @@ def transpile_from_solidity(sol_src_path, main_contract) -> dict:
codes = json.loads(result.stdout)
yul_ast = parse_to_normalized_ast(codes)
cairo_code = transpile_from_yul(yul_ast)
os.remove(sol_src_path_modified)
try:
os.remove(sol_src_path_modified)
except FileNotFoundError:
pass # for reentrancy
return {"cairo_code": cairo_code, "sol_abi": abi}


Expand Down
5 changes: 4 additions & 1 deletion warp/yul/starknet_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ async def invoke_method(


async def deploy_contract(
starknet: StarknetState, program_info: dict, contract_definition, *args: list
starknet: StarknetState,
program_info: dict,
contract_definition: ContractDefinition,
*args: list
) -> str:
evm_calldata = get_ctor_evm_calldata(program_info["sol_abi"], args)
cairo_calldata = get_cairo_calldata(evm_calldata)
Expand Down

0 comments on commit 5ee768b

Please sign in to comment.