From 270d0186c196fa050e02f0c34e8a041f7920f3f3 Mon Sep 17 00:00:00 2001
From: Sergey Pepyakin <s.pepyakin@gmail.com>
Date: Wed, 3 Oct 2018 13:23:50 +0100
Subject: [PATCH] Refine sandbox errors (#860)

---
 substrate/core/executor/src/sandbox.rs       | 62 +++++++++++++-------
 substrate/core/executor/src/wasm_executor.rs | 25 ++++----
 2 files changed, 54 insertions(+), 33 deletions(-)

diff --git a/substrate/core/executor/src/sandbox.rs b/substrate/core/executor/src/sandbox.rs
index 5af389e212f..87fb4083b38 100644
--- a/substrate/core/executor/src/sandbox.rs
+++ b/substrate/core/executor/src/sandbox.rs
@@ -181,21 +181,21 @@ pub struct GuestExternals<'a, FE: SandboxCapabilities + Externals + 'a> {
 	state: u32,
 }
 
-fn trap() -> Trap {
-	TrapKind::Host(Box::new(UserError("Sandbox error"))).into()
+fn trap(msg: &'static str) -> Trap {
+	TrapKind::Host(Box::new(UserError(msg))).into()
 }
 
 fn deserialize_result(serialized_result: &[u8]) -> Result<Option<RuntimeValue>, Trap> {
 	use self::sandbox_primitives::{HostError, ReturnValue};
 	let result_val = Result::<ReturnValue, HostError>::decode(&mut &serialized_result[..])
-		.ok_or_else(|| trap())?;
+		.ok_or_else(|| trap("Decoding Result<ReturnValue, HostError> failed!"))?;
 
 	match result_val {
 		Ok(return_value) => Ok(match return_value {
 			ReturnValue::Unit => None,
 			ReturnValue::Value(typed_value) => Some(RuntimeValue::from(typed_value)),
 		}),
-		Err(HostError) => Err(trap()),
+		Err(HostError) => Err(trap("Supervisor function returned sandbox::HostError")),
 	}
 }
 
@@ -257,7 +257,8 @@ impl<'a, FE: SandboxCapabilities + Externals + 'a> Externals for GuestExternals<
 				let len = (v & 0xFFFFFFFF) as u32;
 				(ptr, len)
 			}
-			_ => return Err(trap()),
+			Ok(_) => return Err(trap("Supervisor function returned unexpected result!")),
+			Err(_) => return Err(trap("Supervisor function trapped!")),
 		};
 
 		let serialized_result_val = self.supervisor_externals
@@ -474,7 +475,12 @@ impl Store {
 		};
 
 		let mem =
-			MemoryInstance::alloc(Pages(initial as usize), maximum).map_err(|_| UserError("Sandbox error"))?;
+			MemoryInstance::alloc(
+				Pages(initial as usize),
+				maximum,
+			)
+			.map_err(|_| UserError("Sandboxed memory allocation error"))?;
+
 		let mem_idx = self.memories.len();
 		self.memories.push(Some(mem));
 		Ok(mem_idx as u32)
@@ -490,8 +496,8 @@ impl Store {
 		self.instances
 			.get(instance_idx as usize)
 			.cloned()
-			.ok_or_else(|| UserError("Sandbox error"))?
-			.ok_or_else(|| UserError("Sandbox error"))
+			.ok_or_else(|| UserError("Trying to access a non-existent instance"))?
+			.ok_or_else(|| UserError("Trying to access a torndown instance"))
 	}
 
 	/// Returns reference to a memory instance by `memory_idx`.
@@ -499,35 +505,47 @@ impl Store {
 	/// # Errors
 	///
 	/// Returns `Err` If `memory_idx` isn't a valid index of an memory or
-	/// memory is already torndown.
+	/// if memory has been torn down.
 	pub fn memory(&self, memory_idx: u32) -> Result<MemoryRef, UserError> {
 		self.memories
 			.get(memory_idx as usize)
 			.cloned()
-			.ok_or_else(|| UserError("Sandbox error"))?
-			.ok_or_else(|| UserError("Sandbox error"))
+			.ok_or_else(|| UserError("Trying to access a non-existent sandboxed memory"))?
+			.ok_or_else(|| UserError("Trying to access a torndown sandboxed memory"))
 	}
 
-	/// Teardown the memory at the specified index.
+	/// Tear down the memory at the specified index.
 	///
 	/// # Errors
 	///
-	/// Returns `Err` if `memory_idx` isn't a valid index of an memory.
+	/// Returns `Err` if `memory_idx` isn't a valid index of an memory or
+	/// if it has been torn down.
 	pub fn memory_teardown(&mut self, memory_idx: u32) -> Result<(), UserError> {
-		if memory_idx as usize >= self.memories.len() {
-			return Err(UserError("Sandbox error"));
+		match self.memories.get_mut(memory_idx as usize) {
+			None => Err(UserError("Trying to teardown a non-existent sandboxed memory")),
+			Some(None) => Err(UserError("Double teardown of a sandboxed memory")),
+			Some(memory) => {
+				*memory = None;
+				Ok(())
+			}
 		}
-		self.memories[memory_idx as usize] = None;
-		Ok(())
 	}
 
-	/// Teardown the instance at the specified index.
+	/// Tear down the instance at the specified index.
+	///
+	/// # Errors
+	///
+	/// Returns `Err` if `instance_idx` isn't a valid index of an instance or
+	/// if it has been torn down.
 	pub fn instance_teardown(&mut self, instance_idx: u32) -> Result<(), UserError> {
-		if instance_idx as usize >= self.instances.len() {
-			return Err(UserError("Sandbox error"));
+		match self.instances.get_mut(instance_idx as usize) {
+			None => Err(UserError("Trying to teardown a non-existent instance")),
+			Some(None) => Err(UserError("Double teardown of an instance")),
+			Some(instance) => {
+				*instance = None;
+				Ok(())
+			}
 		}
-		self.instances[instance_idx as usize] = None;
-		Ok(())
 	}
 
 	fn register_sandbox_instance(&mut self, sandbox_instance: Rc<SandboxInstance>) -> u32 {
diff --git a/substrate/core/executor/src/wasm_executor.rs b/substrate/core/executor/src/wasm_executor.rs
index eddcc2db8d4..4080459fc0e 100644
--- a/substrate/core/executor/src/wasm_executor.rs
+++ b/substrate/core/executor/src/wasm_executor.rs
@@ -352,15 +352,18 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
 		imports_len: usize,
 		state: usize
 	) -> u32 => {
-		let wasm = this.memory.get(wasm_ptr, wasm_len as usize).map_err(|_| UserError("Sandbox error"))?;
-		let raw_env_def = this.memory.get(imports_ptr, imports_len as usize).map_err(|_| UserError("Sandbox error"))?;
+		let wasm = this.memory.get(wasm_ptr, wasm_len as usize)
+			.map_err(|_| UserError("OOB while ext_sandbox_instantiate: wasm"))?;
+		let raw_env_def = this.memory.get(imports_ptr, imports_len as usize)
+			.map_err(|_| UserError("OOB while ext_sandbox_instantiate: imports"))?;
 
 		// Extract a dispatch thunk from instance's table by the specified index.
 		let dispatch_thunk = {
-			let table = this.table.as_ref().ok_or_else(|| UserError("Sandbox error"))?;
+			let table = this.table.as_ref()
+				.ok_or_else(|| UserError("Runtime doesn't have a table; sandbox is unavailable"))?;
 			table.get(dispatch_thunk_idx)
-				.map_err(|_| UserError("Sandbox error"))?
-				.ok_or_else(|| UserError("Sandbox error"))?
+				.map_err(|_| UserError("dispatch_thunk_idx is out of the table bounds"))?
+				.ok_or_else(|| UserError("dispatch_thunk_idx points on an empty table entry"))?
 				.clone()
 		};
 
@@ -382,17 +385,17 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
 
 		trace!(target: "sr-sandbox", "invoke, instance_idx={}", instance_idx);
 		let export = this.memory.get(export_ptr, export_len as usize)
-			.map_err(|_| UserError("Sandbox error"))
+			.map_err(|_| UserError("OOB while ext_sandbox_invoke: export"))
 			.and_then(|b|
 				String::from_utf8(b)
-					.map_err(|_| UserError("Sandbox error"))
+					.map_err(|_| UserError("export name should be a valid utf-8 sequence"))
 			)?;
 
 		// Deserialize arguments and convert them into wasmi types.
 		let serialized_args = this.memory.get(args_ptr, args_len as usize)
-			.map_err(|_| UserError("Sandbox error"))?;
+			.map_err(|_| UserError("OOB while ext_sandbox_invoke: args"))?;
 		let args = Vec::<sandbox_primitives::TypedValue>::decode(&mut &serialized_args[..])
-			.ok_or_else(|| UserError("Sandbox error"))?
+			.ok_or_else(|| UserError("Can't decode serialized arguments for the invocation"))?
 			.into_iter()
 			.map(Into::into)
 			.collect::<Vec<_>>();
@@ -406,11 +409,11 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
 				// Serialize return value and write it back into the memory.
 				sandbox_primitives::ReturnValue::Value(val.into()).using_encoded(|val| {
 					if val.len() > return_val_len as usize {
-						Err(UserError("Sandbox error"))?;
+						Err(UserError("Return value buffer is too small"))?;
 					}
 					this.memory
 						.set(return_val_ptr, val)
-						.map_err(|_| UserError("Sandbox error"))?;
+						.map_err(|_| UserError("Return value buffer is OOB"))?;
 					Ok(sandbox_primitives::ERR_OK)
 				})
 			}
-- 
GitLab