diff --git a/bins/revme/src/debugger/ctrl/ctrl.rs b/bins/revme/src/debugger/ctrl/ctrl.rs index 5ecaef1536..b9bc472d53 100644 --- a/bins/revme/src/debugger/ctrl/ctrl.rs +++ b/bins/revme/src/debugger/ctrl/ctrl.rs @@ -144,7 +144,7 @@ impl Inspector for Controller { println!( "call_depth:{} PC:{} Opcode: {:#x} {:?} gas(spend,remaining):({},{})\n\ Stack:{}", - interp.call_depth, + data.subroutine.depth(), interp.program_counter(), opcode, OPCODE_JUMPMAP[opcode as usize].unwrap_or("Invalid"), diff --git a/bins/revme/src/statetest/trace.rs b/bins/revme/src/statetest/trace.rs index 0cc2a4d89d..ac9cc968b0 100644 --- a/bins/revme/src/statetest/trace.rs +++ b/bins/revme/src/statetest/trace.rs @@ -50,7 +50,7 @@ impl Inspector for CustomPrintTracer { println!( "depth:{}, PC:{}, gas:{:#x}({}), OPCODE: {:?}({:?}) refund:{:#x}({}) Stack:{:?}, Data:", - interp.call_depth, + data.subroutine.depth(), interp.program_counter(), interp.gas.remaining()+self.full_gas_block-self.reduced_gas_block, interp.gas.remaining()+self.full_gas_block-self.reduced_gas_block, diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index a4caaf62d2..e644a97015 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -33,3 +33,4 @@ secp256k1 = ["revm_precompiles/secp256k1"] k256 = ["revm_precompiles/k256_ecrecover"] web3db = ["futures", "tokio", "parking_lot", "web3"] with-serde = ["serde", "primitive-types/serde", "hex", "hex/serde"] +memory_limit = [] diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs index 9f1bd79f4b..91da841df2 100644 --- a/crates/revm/src/evm_impl.rs +++ b/crates/revm/src/evm_impl.rs @@ -370,8 +370,17 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, inputs.caller, inputs.value, ); - let mut interp = - Interpreter::new::(contract, gas.limit(), self.data.subroutine.depth()); + + #[cfg(feature = "memory_limit")] + let mut interp = Interpreter::new_with_memory_limit::( + contract, + gas.limit(), + self.data.env.cfg.memory_limit, + ); + + #[cfg(not(feature = "memory_limit"))] + let mut interp = Interpreter::new::(contract, gas.limit()); + if Self::INSPECT { self.inspector .initialize_interp(&mut interp, &mut self.data, false); // TODO fix is_static @@ -518,8 +527,17 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, // Create interpreter and execute subcall let contract = Contract::new_with_context::(inputs.input.clone(), code, &inputs.context); - let mut interp = - Interpreter::new::(contract, inputs.gas_limit, self.data.subroutine.depth()); + + #[cfg(feature = "memory_limit")] + let mut interp = Interpreter::new_with_memory_limit::( + contract, + gas.limit(), + self.data.env.cfg.memory_limit, + ); + + #[cfg(not(feature = "memory_limit"))] + let mut interp = Interpreter::new::(contract, gas.limit()); + if Self::INSPECT { self.inspector .initialize_interp(&mut interp, &mut self.data, false); // TODO fix is_static diff --git a/crates/revm/src/instructions/macros.rs b/crates/revm/src/instructions/macros.rs index bf98036930..d1ad4f1d82 100644 --- a/crates/revm/src/instructions/macros.rs +++ b/crates/revm/src/instructions/macros.rs @@ -53,6 +53,11 @@ macro_rules! memory_resize { if let Some(new_size) = crate::interpreter::memory::next_multiple_of_32(offset.saturating_add(len)) { + #[cfg(feature = "memory_limit")] + if new_size > ($interp.memory_limit as usize) { + return Return::OutOfGas; + } + if new_size > $interp.memory.len() { if crate::USE_GAS { let num_bytes = new_size / 32; diff --git a/crates/revm/src/interpreter.rs b/crates/revm/src/interpreter.rs index de66a4f4d9..d2a036f9cd 100644 --- a/crates/revm/src/interpreter.rs +++ b/crates/revm/src/interpreter.rs @@ -31,12 +31,14 @@ pub struct Interpreter { pub return_data_buffer: Bytes, /// Return value. pub return_range: Range, - /// used only for inspector. - pub call_depth: u64, + /// Memory limit. See [`crate::CfgEnv`]. + #[cfg(feature = "memory_limit")] + pub memory_limit: u64, } impl Interpreter { - pub fn new(contract: Contract, gas_limit: u64, call_depth: u64) -> Self { + #[cfg(not(feature = "memory_limit"))] + pub fn new(contract: Contract, gas_limit: u64) -> Self { Self { program_counter: contract.code.as_ptr(), return_range: Range::default(), @@ -45,10 +47,27 @@ impl Interpreter { return_data_buffer: Bytes::new(), contract, gas: Gas::new(gas_limit), - call_depth, - //times: [(std::time::Duration::ZERO, 0); 256], } } + + #[cfg(feature = "memory_limit")] + pub fn new_with_memory_limit( + contract: Contract, + gas_limit: u64, + memory_limit: u64, + ) -> Self { + Self { + program_counter: contract.code.as_ptr(), + return_range: Range::default(), + memory: Memory::new(), + stack: Stack::new(), + return_data_buffer: Bytes::new(), + contract, + gas: Gas::new(gas_limit), + memory_limit, + } + } + pub fn contract(&self) -> &Contract { &self.contract } diff --git a/crates/revm/src/models.rs b/crates/revm/src/models.rs index 86be66f85a..2c62069ba1 100644 --- a/crates/revm/src/models.rs +++ b/crates/revm/src/models.rs @@ -218,18 +218,27 @@ pub struct TxEnv { pub struct CfgEnv { pub chain_id: U256, pub spec_id: SpecId, - /// if all precompiles have some balance we can ignore initial fetching them from db. - /// this is clearly making noice if we use debugger and it is not really needed on mainnet. - /// default is false in most cases it is safe to be set to true, it depends on chain. + /// If all precompiles have some balance we can skip initially fetching them from the database. + /// This is is not really needed on mainnet, and defaults to false, but in most cases it is + /// safe to be set to `true`, depending on the chain. pub perf_all_precompiles_have_balance: bool, + /// A hard memory limit in bytes beyond which [Memory] cannot be resized. + /// + /// In cases where the gas limit may be extraordinarily high, it is recommended to set this to + /// a sane value to prevent memory allocation panics. Defaults to `2^32 - 1` bytes per + /// EIP-1985. + #[cfg(feature = "memory_limit")] + pub memory_limit: u64, } impl Default for CfgEnv { fn default() -> CfgEnv { CfgEnv { - chain_id: 1.into(), //mainnet is 1 + chain_id: 1.into(), spec_id: SpecId::LATEST, perf_all_precompiles_have_balance: false, + #[cfg(feature = "memory_limit")] + memory_limit: 2u64.pow(32) - 1, } } } @@ -239,7 +248,7 @@ impl Default for BlockEnv { BlockEnv { gas_limit: U256::MAX, number: 0.into(), - coinbase: H160::zero(), //zero address + coinbase: H160::zero(), timestamp: U256::one(), difficulty: U256::zero(), basefee: U256::zero(),