From 3795f5cd03288405335d4fb0c46c239dbf4c7e60 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 4 Apr 2023 15:58:32 +0200 Subject: [PATCH] Track call depth --- packages/vm/src/environment.rs | 41 ++++++++++++++++++++++++++++-- packages/vm/src/errors/vm_error.rs | 12 +++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/packages/vm/src/environment.rs b/packages/vm/src/environment.rs index 4072b3fbbd..02270726ec 100644 --- a/packages/vm/src/environment.rs +++ b/packages/vm/src/environment.rs @@ -9,6 +9,13 @@ use wasmer_middlewares::metering::{get_remaining_points, set_remaining_points, M use crate::backend::{BackendApi, GasInfo, Querier, Storage}; use crate::errors::{VmError, VmResult}; +/// Keep this as low as necessary to avoid deepy nested errors like this: +/// +/// ```plain +/// RuntimeErr { msg: "Wasmer runtime error: RuntimeError: Error executing Wasm: Wasmer runtime error: RuntimeError: Error executing Wasm: Wasmer runtime error: RuntimeError: Error executing Wasm: Wasmer runtime error: RuntimeError: Error executing Wasm: Wasmer runtime error: RuntimeError: Maximum call depth exceeded." } +/// ``` +const MAX_CALL_DEPTH: usize = 2; + /// Never can never be instantiated. /// Replace this with the [never primitive type](https://doc.rust-lang.org/std/primitive.never.html) when stable. #[derive(Debug)] @@ -167,7 +174,8 @@ impl Environment { let func = instance.exports.get_function(name)?; Ok(func.clone()) })?; - func.call(args).map_err(|runtime_err| -> VmError { + self.increment_call_depth()?; + let res = func.call(args).map_err(|runtime_err| -> VmError { self.with_wasmer_instance::<_, Never>(|instance| { let err: VmError = match get_remaining_points(instance) { MeteringPoints::Remaining(_) => VmError::from(runtime_err), @@ -176,7 +184,9 @@ impl Environment { Err(err) }) .unwrap_err() // with_wasmer_instance can only succeed if the callback succeeds - }) + }); + self.decrement_call_depth(); + res } pub fn call_function0(&self, name: &str, args: &[Val]) -> VmResult<()> { @@ -237,6 +247,31 @@ impl Environment { }) } + /// Increments the call depth by 1 and returns the new value + pub fn increment_call_depth(&self) -> VmResult { + let new = self.with_context_data_mut(|context_data| { + let new = context_data.call_depth + 1; + context_data.call_depth = new; + new + }); + if new > MAX_CALL_DEPTH { + return Err(VmError::max_call_depth_exceeded()); + } + Ok(new) + } + + /// Decrements the call depth by 1 and returns the new value + pub fn decrement_call_depth(&self) -> usize { + self.with_context_data_mut(|context_data| { + let new = context_data + .call_depth + .checked_sub(1) + .expect("Call depth < 0. This is a bug."); + context_data.call_depth = new; + new + }) + } + pub fn get_gas_left(&self) -> u64 { self.with_wasmer_instance(|instance| { Ok(match get_remaining_points(instance) { @@ -316,6 +351,7 @@ pub struct ContextData { gas_state: GasState, storage: Option, storage_readonly: bool, + call_depth: usize, querier: Option, /// A non-owning link to the wasmer instance wasmer_instance: Option>, @@ -327,6 +363,7 @@ impl ContextData { gas_state: GasState::with_limit(gas_limit), storage: None, storage_readonly: true, + call_depth: 0, querier: None, wasmer_instance: None, } diff --git a/packages/vm/src/errors/vm_error.rs b/packages/vm/src/errors/vm_error.rs index 6140799bc4..51f472cf7d 100644 --- a/packages/vm/src/errors/vm_error.rs +++ b/packages/vm/src/errors/vm_error.rs @@ -145,6 +145,11 @@ pub enum VmError { #[cfg(feature = "backtraces")] backtrace: Backtrace, }, + #[error("Maximum call depth exceeded.")] + MaxCallDepthExceeded { + #[cfg(feature = "backtraces")] + backtrace: Backtrace, + }, } impl VmError { @@ -314,6 +319,13 @@ impl VmError { backtrace: Backtrace::capture(), } } + + pub(crate) fn max_call_depth_exceeded() -> Self { + VmError::MaxCallDepthExceeded { + #[cfg(feature = "backtraces")] + backtrace: Backtrace::capture(), + } + } } impl From for VmError {