From bc23c12d00ca86ca8ba440d6f095334b6f1006a1 Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 10 Jul 2024 16:37:16 +0200 Subject: [PATCH 1/3] fix(EOF): MIN_CALLEE_GAS light failure, static-mode check --- crates/interpreter/src/gas/constants.rs | 1 + crates/interpreter/src/instruction_result.rs | 2 ++ .../interpreter/src/instructions/contract.rs | 23 ++++++++++++------- crates/interpreter/src/interpreter.rs | 6 +++++ 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/crates/interpreter/src/gas/constants.rs b/crates/interpreter/src/gas/constants.rs index c32abcfae7..57f50d9379 100644 --- a/crates/interpreter/src/gas/constants.rs +++ b/crates/interpreter/src/gas/constants.rs @@ -54,3 +54,4 @@ pub const PER_CONTRACT_CODE_BASE_COST: u64 = 2400; pub const INITCODE_WORD_COST: u64 = 2; pub const CALL_STIPEND: u64 = 2300; +pub const MIN_CALLEE_GAS: u64 = CALL_STIPEND; diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index 47b399b5b1..0cdc694c6f 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -33,7 +33,9 @@ pub enum InstructionResult { PrecompileOOG, InvalidOperandOOG, OpcodeNotFound, + /// Transferring value with CALL/CALLCODE is not possible in static mode. CallNotAllowedInsideStatic, + /// State change attempted in static mode. StateChangeDuringStaticCall, InvalidFEOpcode, InvalidJump, diff --git a/crates/interpreter/src/instructions/contract.rs b/crates/interpreter/src/instructions/contract.rs index a278a220b4..7bdc6fa1c2 100644 --- a/crates/interpreter/src/instructions/contract.rs +++ b/crates/interpreter/src/instructions/contract.rs @@ -3,7 +3,7 @@ mod call_helpers; pub use call_helpers::{calc_call_gas, get_memory_input_and_out_ranges, resize_memory}; use crate::{ - gas::{self, cost_per_word, EOF_CREATE_GAS, KECCAK256WORD}, + gas::{self, cost_per_word, EOF_CREATE_GAS, KECCAK256WORD, MIN_CALLEE_GAS}, interpreter::Interpreter, primitives::{ eof::EofHeader, keccak256, Address, BerlinSpec, Bytes, Eof, Spec, SpecId::*, B256, U256, @@ -170,7 +170,6 @@ pub fn extcall_gas_calc( return None; }; - // TODO(EOF) is_empty should only be checked on delegatecall let call_cost = gas::call_cost( BerlinSpec::SPEC_ID, transfers_value, @@ -184,10 +183,16 @@ pub fn extcall_gas_calc( let gas_reduce = max(interpreter.gas.remaining() / 64, 5000); let gas_limit = interpreter.gas().remaining().saturating_sub(gas_reduce); - if gas_limit < 2300 { - interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic; - // TODO(EOF) error; - // interpreter.instruction_result = InstructionResult::CallGasTooLow; + // The MIN_CALLEE_GAS rule is a replacement for stipend: + // it simplifies the reasoning about the gas costs and is + // applied uniformly for all introduced EXT*CALL instructions. + // + // If Gas available to callee is less than MIN_CALLEE_GAS trigger light failure (Same as Revert). + if gas_limit < MIN_CALLEE_GAS { + // Push 1 to stack to indicate that call light failed. + // It is safe to ignore stack overflow error as we already popped multiple values from stack. + let _ = interpreter.stack_mut().push(U256::from(1)); + // Return none to continue execution. return None; } @@ -226,11 +231,14 @@ pub fn extcall(interpreter: &mut Interpreter, host pop!(interpreter, value); let has_transfer = value != U256::ZERO; + if interpreter.is_static && has_transfer { + interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic; + return; + } let Some(gas_limit) = extcall_gas_calc(interpreter, host, target_address, has_transfer) else { return; }; - // TODO Check if static and value 0 // Call host to interact with target contract interpreter.next_action = InterpreterAction::Call { @@ -266,7 +274,6 @@ pub fn extdelegatecall(interpreter: &mut Interpret let Some(gas_limit) = extcall_gas_calc(interpreter, host, target_address, false) else { return; }; - // TODO Check if static and value 0 // Call host to interact with target contract interpreter.next_action = InterpreterAction::Call { diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 47e041473f..fbfac34a3c 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -329,6 +329,12 @@ impl Interpreter { &self.stack } + /// Returns a mutable reference to the interpreter's stack. + #[inline] + pub fn stack_mut(&mut self) -> &mut Stack { + &mut self.stack + } + /// Returns the current program counter. #[inline] pub fn program_counter(&self) -> usize { From fa58601277ffdd959cb35ca5336f25d6bf28b4b1 Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 10 Jul 2024 16:41:52 +0200 Subject: [PATCH 2/3] clear buffer --- crates/interpreter/src/instructions/contract.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/crates/interpreter/src/instructions/contract.rs b/crates/interpreter/src/instructions/contract.rs index 7bdc6fa1c2..dd5e865b3b 100644 --- a/crates/interpreter/src/instructions/contract.rs +++ b/crates/interpreter/src/instructions/contract.rs @@ -183,19 +183,6 @@ pub fn extcall_gas_calc( let gas_reduce = max(interpreter.gas.remaining() / 64, 5000); let gas_limit = interpreter.gas().remaining().saturating_sub(gas_reduce); - // The MIN_CALLEE_GAS rule is a replacement for stipend: - // it simplifies the reasoning about the gas costs and is - // applied uniformly for all introduced EXT*CALL instructions. - // - // If Gas available to callee is less than MIN_CALLEE_GAS trigger light failure (Same as Revert). - if gas_limit < MIN_CALLEE_GAS { - // Push 1 to stack to indicate that call light failed. - // It is safe to ignore stack overflow error as we already popped multiple values from stack. - let _ = interpreter.stack_mut().push(U256::from(1)); - // Return none to continue execution. - return None; - } - gas!(interpreter, gas_limit, None); Some(gas_limit) } From 212ca3dbef790122beecab46508fe4042ce9540b Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 10 Jul 2024 17:13:41 +0200 Subject: [PATCH 3/3] readd min caller gas --- crates/interpreter/src/instructions/contract.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/interpreter/src/instructions/contract.rs b/crates/interpreter/src/instructions/contract.rs index dd5e865b3b..d10cb05b34 100644 --- a/crates/interpreter/src/instructions/contract.rs +++ b/crates/interpreter/src/instructions/contract.rs @@ -183,6 +183,20 @@ pub fn extcall_gas_calc( let gas_reduce = max(interpreter.gas.remaining() / 64, 5000); let gas_limit = interpreter.gas().remaining().saturating_sub(gas_reduce); + // The MIN_CALLEE_GAS rule is a replacement for stipend: + // it simplifies the reasoning about the gas costs and is + // applied uniformly for all introduced EXT*CALL instructions. + // + // If Gas available to callee is less than MIN_CALLEE_GAS trigger light failure (Same as Revert). + if gas_limit < MIN_CALLEE_GAS { + // Push 1 to stack to indicate that call light failed. + // It is safe to ignore stack overflow error as we already popped multiple values from stack. + let _ = interpreter.stack_mut().push(U256::from(1)); + interpreter.return_data_buffer.clear(); + // Return none to continue execution. + return None; + } + gas!(interpreter, gas_limit, None); Some(gas_limit) }