From 202985b24f79b354cfdfa2e5c5288e5fbc3f1a41 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 11 May 2023 14:45:32 +0200 Subject: [PATCH] Fix CALL gas (#1030) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix call gas + precompiles can be non-existent * Memory expansion before call gas * Minor * Add call_gas.asm * EIP-2200: If gasleft is less than or equal to gas stipend, fail the current call frame with ‘out of gas’ exception. --- evm/src/cpu/kernel/aggregator.rs | 1 + evm/src/cpu/kernel/asm/core/call.asm | 125 +++++------------- evm/src/cpu/kernel/asm/core/call_gas.asm | 92 +++++++++++++ evm/src/cpu/kernel/asm/core/util.asm | 7 - .../kernel/asm/mpt/storage/storage_write.asm | 1 + 5 files changed, 126 insertions(+), 100 deletions(-) create mode 100644 evm/src/cpu/kernel/asm/core/call_gas.asm diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index 976aa86f..1091c9e3 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -25,6 +25,7 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/bignum/util.asm"), include_str!("asm/core/bootloader.asm"), include_str!("asm/core/call.asm"), + include_str!("asm/core/call_gas.asm"), include_str!("asm/core/create.asm"), include_str!("asm/core/create_addresses.asm"), include_str!("asm/core/create_contract_account.asm"), diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 6a80015f..0184f365 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -11,19 +11,19 @@ global sys_call: MUL // Cheaper than AND %jumpi(fault_exception) + %stack (kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) -> + (args_size, args_offset, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) + %checked_mem_expansion + %stack (kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) -> + (ret_size, ret_offset, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) + %checked_mem_expansion + SWAP2 // stack: address, gas, kexit_info, value, args_offset, args_size, ret_offset, ret_size %u256_to_addr // Truncate to 160 bits DUP1 %insert_accessed_addresses - %call_charge_gas - - %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (args_size, args_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion - %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (ret_size, ret_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion + %call_charge_gas(1, 1) %create_context // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size @@ -56,19 +56,19 @@ global sys_call: // given account. In particular the storage remains the same. global sys_callcode: // stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size + %stack (kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) -> + (args_size, args_offset, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) + %checked_mem_expansion + %stack (kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) -> + (ret_size, ret_offset, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) + %checked_mem_expansion + SWAP2 // stack: address, gas, kexit_info, value, args_offset, args_size, ret_offset, ret_size %u256_to_addr // Truncate to 160 bits DUP1 %insert_accessed_addresses - %call_charge_gas - - %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (args_size, args_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion - %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (ret_size, ret_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion + %call_charge_gas(1, 0) // stack: kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size %create_context @@ -104,6 +104,13 @@ global sys_callcode: // CALL if the value sent is not 0. global sys_staticcall: // stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size + %stack (kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) -> + (args_size, args_offset, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) + %checked_mem_expansion + %stack (kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) -> + (ret_size, ret_offset, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) + %checked_mem_expansion + SWAP2 // stack: address, gas, kexit_info, args_offset, args_size, ret_offset, ret_size %u256_to_addr // Truncate to 160 bits @@ -111,14 +118,7 @@ global sys_staticcall: // Add a value of 0 to the stack. Slightly inefficient but that way we can reuse %call_charge_gas. %stack (cold_access, address, gas, kexit_info) -> (cold_access, address, gas, kexit_info, 0) - %call_charge_gas - - %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (args_size, args_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion - %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (ret_size, ret_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion + %call_charge_gas(0, 1) // stack: kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size %create_context @@ -152,6 +152,13 @@ global sys_staticcall: // value remain the same. global sys_delegatecall: // stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size + %stack (kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) -> + (args_size, args_offset, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) + %checked_mem_expansion + %stack (kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) -> + (ret_size, ret_offset, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) + %checked_mem_expansion + SWAP2 // stack: address, gas, kexit_info, args_offset, args_size, ret_offset, ret_size %u256_to_addr // Truncate to 160 bits @@ -159,14 +166,7 @@ global sys_delegatecall: // Add a value of 0 to the stack. Slightly inefficient but that way we can reuse %call_charge_gas. %stack (cold_access, address, gas, kexit_info) -> (cold_access, address, gas, kexit_info, 0) - %call_charge_gas - - %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (args_size, args_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion - %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (ret_size, ret_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion + %call_charge_gas(0, 0) // stack: kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size %create_context @@ -336,67 +336,6 @@ global after_call_instruction: %%after: %endmacro -// Charge gas for *call opcodes and return the sub-context gas limit. -// Doesn't include memory expansion costs. -%macro call_charge_gas - // Compute C_aaccess - // stack: cold_access, address, gas, kexit_info, value - %mul_const(@GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS) - %add_const(@GAS_WARMACCESS) - - // Compute C_xfer - // stack: Caaccess, address, gas, kexit_info, value - DUP5 ISZERO %not_bit - // stack: value≠0, Caaccess, address, gas, kexit_info, value - DUP1 - %mul_const(@GAS_CALLVALUE) - - // Compute C_new - // stack: Cxfer, value≠0, Caaccess, address, gas, kexit_info, value - SWAP1 - // stack: value≠0, Cxfer, Caaccess, address, gas, kexit_info, value - DUP4 %is_dead MUL - // stack: is_dead(address) and value≠0, Cxfer, Caaccess, address, gas, kexit_info, value - %mul_const(@GAS_NEWACCOUNT) - // stack: Cnew, Cxfer, Caaccess, address, gas, kexit_info, value - - // Compute C_extra - ADD ADD - - // Compute C_gascap - // stack: Cextra, address, gas, kexit_info, value - DUP4 %leftover_gas - // stack: leftover_gas, Cextra, address, gas, kexit_info, value - DUP2 DUP2 LT - // stack: leftover_gas=Cextra, (leftover_gas=Cextra, (leftover_gas=Cextra, (leftover_gas (Cextra, Cgascap, Cgascap) - ADD - %stack (C_call, Cgascap, address, gas, kexit_info, value) -> - (C_call, kexit_info, Cgascap, address, gas, value) - %charge_gas - - // Compute C_callgas - %stack (kexit_info, Cgascap, address, gas, value) -> - (Cgascap, address, gas, kexit_info, value) - DUP5 ISZERO %not_bit - // stack: value!=0, Cgascap, address, gas, kexit_info, value - %mul_const(@GAS_CALLSTIPEND) ADD - %stack (C_callgas, address, gas, kexit_info, value) -> - (kexit_info, C_callgas, address, value) -%endmacro - // Checked memory expansion. %macro checked_mem_expansion // stack: size, offset, kexit_info diff --git a/evm/src/cpu/kernel/asm/core/call_gas.asm b/evm/src/cpu/kernel/asm/core/call_gas.asm new file mode 100644 index 00000000..4dcbcec1 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/call_gas.asm @@ -0,0 +1,92 @@ +%macro call_charge_gas(is_call_or_callcode, is_call_or_staticcall) + %stack (cold_access, address, gas, kexit_info, value) -> + ($is_call_or_callcode, $is_call_or_staticcall, cold_access, address, gas, kexit_info, value, %%after) + %jump(call_charge_gas) +%%after: + // stack: kexit_info, C_callgas, address, value +%endmacro + +// Charge gas for *call opcodes and return the sub-context gas limit. +// Doesn't include memory expansion costs. +global call_charge_gas: + // Compute C_aaccess + // stack: is_call_or_callcode, is_call_or_staticcall, cold_access, address, gas, kexit_info, value, retdest + SWAP2 + // stack: cold_access, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + %mul_const(@GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS) + %add_const(@GAS_WARMACCESS) + // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + DUP4 + // stack: is_call_or_callcode, cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + %jumpi(xfer_cost) +after_xfer_cost: + // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + DUP2 + %jumpi(new_cost) +after_new_cost: + %stack (Cextra, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest) -> + (Cextra, address, gas, kexit_info, value, retdest) + // Compute C_gascap + // stack: Cextra, address, gas, kexit_info, value, retdest + DUP4 %leftover_gas + // stack: leftover_gas, Cextra, address, gas, kexit_info, value, retdest + DUP2 DUP2 LT + // stack: leftover_gas=Cextra, (leftover_gas=Cextra, (leftover_gas=Cextra, (leftover_gas (Cextra, Cgascap, Cgascap) + ADD + %stack (C_call, Cgascap, address, gas, kexit_info, value) -> + (C_call, kexit_info, Cgascap, address, gas, value) + %charge_gas + + // Compute C_callgas + %stack (kexit_info, Cgascap, address, gas, value) -> + (Cgascap, address, gas, kexit_info, value) + DUP5 ISZERO %not_bit + // stack: value!=0, Cgascap, address, gas, kexit_info, value, retdest + %mul_const(@GAS_CALLSTIPEND) ADD + %stack (C_callgas, address, gas, kexit_info, value, retdest) -> + (retdest, kexit_info, C_callgas, address, value) + JUMP + +xfer_cost: + // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + DUP7 + // stack: value, cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + %jumpi(xfer_cost_nonzero) + // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + %jump(after_xfer_cost) +xfer_cost_nonzero: + // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + %add_const(@GAS_CALLVALUE) + // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + %jump(after_xfer_cost) + +new_cost: + // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + DUP7 + // stack: value, cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + %jumpi(new_cost_transfers_value) + // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + %jump(after_new_cost) +new_cost_transfers_value: + // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + DUP4 %is_dead + %jumpi(new_cost_nonzero) + // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + %jump(after_new_cost) +new_cost_nonzero: + // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest + %add_const(@GAS_NEWACCOUNT) + %jump(after_new_cost) diff --git a/evm/src/cpu/kernel/asm/core/util.asm b/evm/src/cpu/kernel/asm/core/util.asm index 04989459..d64c6059 100644 --- a/evm/src/cpu/kernel/asm/core/util.asm +++ b/evm/src/cpu/kernel/asm/core/util.asm @@ -38,14 +38,7 @@ // Returns 1 if the account is non-existent, 0 otherwise. %macro is_non_existent // stack: addr - DUP1 - // stack: addr, addr %mpt_read_state_trie ISZERO - SWAP1 - // stack: addr, zero_state_trie - %is_precompile ISZERO - // stack: not_precompile, zero_state_trie - MUL // Cheaper than AND %endmacro // Returns 1 if the account is empty, 0 otherwise. diff --git a/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm b/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm index 6383b871..109a5577 100644 --- a/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm +++ b/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm @@ -5,6 +5,7 @@ global sys_sstore: %check_static + DUP1 %leftover_gas %le_const(@GAS_CALLSTIPEND) %jumpi(fault_exception) %stack (kexit_info, slot, value) -> (slot, kexit_info, slot, value) %sload_current %address