diff --git a/crates/integration/contracts/MLoad.sol b/crates/integration/contracts/MLoad.sol index 74ee672..d93daec 100644 --- a/crates/integration/contracts/MLoad.sol +++ b/crates/integration/contracts/MLoad.sol @@ -20,7 +20,7 @@ pragma solidity ^0.8.28; "dest": { "Instantiated": 0 }, - "data": "e2179b8e" + "data": "0be0e4a60000000000000000000000000000000000000000000000000000000000000000" } } ] @@ -29,12 +29,12 @@ pragma solidity ^0.8.28; contract MLoad { constructor() payable { - assert(g() == 0); + assert(loadAt(0) == 0); } - function g() public payable returns (uint m) { + function loadAt(uint _offset) public payable returns (uint m) { assembly { - m := mload(0) + m := mload(_offset) } } } diff --git a/crates/integration/src/cases.rs b/crates/integration/src/cases.rs index 7e2b1a5..adc3e72 100644 --- a/crates/integration/src/cases.rs +++ b/crates/integration/src/cases.rs @@ -234,6 +234,15 @@ sol!( ); case!("MCopy.sol", MCopy, memcpyCall, memcpy, payload: Bytes); +sol!( + contract MLoad { + constructor() payable; + + function loadAt(uint _offset) public payable returns (uint m); + } +); +case!("MLoad.sol", MLoad, loadAtCall, load_at, _offset: U256); + sol!( contract Call { function value_transfer(address payable destination) public payable; diff --git a/crates/integration/src/tests.rs b/crates/integration/src/tests.rs index a70f72e..d559fb5 100644 --- a/crates/integration/src/tests.rs +++ b/crates/integration/src/tests.rs @@ -671,3 +671,41 @@ fn invalid_opcode_works() { assert_eq!(result.weight_consumed, GAS_LIMIT); } + +/// Load from heap memory using an out of bounds offset and expect the +/// contract to hit the `invalid` syscall to use all gas (like on EVM). +/// +/// The offset is picked such that a regular truncate would be in bounds. +#[test] +fn safe_truncate_int_to_xlen_works() { + let offset = 0x10000000_00000000u64; + let data = Contract::load_at(Uint::from(offset)).calldata; + let mut actions = instantiate("contracts/MLoad.sol", "MLoad"); + actions.append(&mut vec![ + Call { + origin: TestAddress::Alice, + dest: TestAddress::Instantiated(0), + value: 0, + gas_limit: None, + storage_deposit_limit: None, + data, + }, + VerifyCall(VerifyCallExpectation { + success: false, + ..Default::default() + }), + ]); + + let results = Specs { + actions, + differential: true, + ..Default::default() + } + .run(); + + let CallResult::Exec { result, .. } = results.last().unwrap() else { + unreachable!() + }; + + assert_eq!(result.weight_consumed, GAS_LIMIT); +}