From 07268022cc5ef6c8b104d154c742012f6934fe4a Mon Sep 17 00:00:00 2001
From: Sergei Pepyakin <s.pepyakin@gmail.com>
Date: Fri, 19 Apr 2019 19:35:11 +0200
Subject: [PATCH] contracts: Validate code before deployment (#2330)

* Validate module before storing it in code_cache.

* Bump version.
---
 substrate/Cargo.lock                          | 11 +++++++
 substrate/node/runtime/Cargo.toml             |  3 ++
 substrate/node/runtime/src/lib.rs             |  2 +-
 substrate/node/runtime/wasm/Cargo.lock        | 10 ++++++
 substrate/node/runtime/wasm/Cargo.toml        |  5 ++-
 substrate/srml/contract/Cargo.toml            |  5 +++
 .../srml/contract/src/wasm/code_cache.rs      |  2 --
 substrate/srml/contract/src/wasm/prepare.rs   | 31 ++++++++++++++-----
 8 files changed, 57 insertions(+), 12 deletions(-)

diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock
index be655691971..5e35d98feec 100644
--- a/substrate/Cargo.lock
+++ b/substrate/Cargo.lock
@@ -3233,6 +3233,7 @@ dependencies = [
  "srml-timestamp 1.0.0",
  "substrate-primitives 1.0.0",
  "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasmi-validation 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -5071,6 +5072,15 @@ dependencies = [
  "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "wasmi-validation"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "websocket"
 version = "0.22.2"
@@ -5603,6 +5613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1"
 "checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3"
 "checksum wasmi 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21ef487a11df1ed468cf613c78798c26282da5c30e9d49f824872d4c77b47d1d"
+"checksum wasmi-validation 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab380192444b3e8522ae79c0a1976e42a82920916ccdfbce3def89f456ea33f3"
 "checksum websocket 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d2c67346c042adbd4f5b2a49700e340befc5b772094fec8d36df6b825523d933"
 "checksum which 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e84a603e7e0b1ce1aa1ee2b109c7be00155ce52df5081590d1ffb93f4f515cb2"
 "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
diff --git a/substrate/node/runtime/Cargo.toml b/substrate/node/runtime/Cargo.toml
index 9cdceb96ac9..f8e0e047bc9 100644
--- a/substrate/node/runtime/Cargo.toml
+++ b/substrate/node/runtime/Cargo.toml
@@ -41,6 +41,9 @@ consensus_authorities = { package = "substrate-consensus-authorities", path = ".
 
 [features]
 default = ["std"]
+core = [
+	"contract/core",
+]
 std = [
 	"parity-codec/std",
 	"substrate-primitives/std",
diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs
index 7239e14aa1b..3a3ad41c289 100644
--- a/substrate/node/runtime/src/lib.rs
+++ b/substrate/node/runtime/src/lib.rs
@@ -59,7 +59,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
 	spec_name: create_runtime_str!("node"),
 	impl_name: create_runtime_str!("substrate-node"),
 	authoring_version: 10,
-	spec_version: 62,
+	spec_version: 63,
 	impl_version: 65,
 	apis: RUNTIME_API_VERSIONS,
 };
diff --git a/substrate/node/runtime/wasm/Cargo.lock b/substrate/node/runtime/wasm/Cargo.lock
index af458157776..243a60fb254 100644
--- a/substrate/node/runtime/wasm/Cargo.lock
+++ b/substrate/node/runtime/wasm/Cargo.lock
@@ -2226,6 +2226,7 @@ dependencies = [
  "srml-system 1.0.0",
  "srml-timestamp 1.0.0",
  "substrate-primitives 1.0.0",
+ "wasmi-validation 0.1.0",
 ]
 
 [[package]]
@@ -3203,6 +3204,15 @@ dependencies = [
  "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "wasmi-validation"
+version = "0.1.0"
+dependencies = [
+ "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "winapi"
 version = "0.2.8"
diff --git a/substrate/node/runtime/wasm/Cargo.toml b/substrate/node/runtime/wasm/Cargo.toml
index 8fd90890e95..b6490a047ed 100644
--- a/substrate/node/runtime/wasm/Cargo.toml
+++ b/substrate/node/runtime/wasm/Cargo.toml
@@ -12,7 +12,10 @@ crate-type = ["cdylib"]
 node-runtime = { path = "..", default-features = false }
 
 [features]
-default = []
+default = ["core"]
+core = [
+	"node-runtime/core",
+]
 std = [
 	"node-runtime/std",
 ]
diff --git a/substrate/srml/contract/Cargo.toml b/substrate/srml/contract/Cargo.toml
index 2338aabe0d2..be4034c849e 100644
--- a/substrate/srml/contract/Cargo.toml
+++ b/substrate/srml/contract/Cargo.toml
@@ -10,6 +10,7 @@ serde_derive = { version = "1.0", optional = true }
 pwasm-utils = { version = "0.6.1", default-features = false }
 parity-codec = { version = "3.3", default-features = false, features = ["derive"] }
 parity-wasm = { version = "0.31", default-features = false }
+wasmi-validation = { version = "0.1", default-features = false }
 substrate-primitives = { path = "../../core/primitives", default-features = false }
 runtime-primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false }
 runtime-io = { package = "sr-io", path = "../../core/sr-io", default-features = false }
@@ -28,6 +29,9 @@ balances = { package = "srml-balances", path = "../balances" }
 
 [features]
 default = ["std"]
+core = [
+	"wasmi-validation/core",
+]
 std = [
 	"serde",
 	"serde_derive",
@@ -42,4 +46,5 @@ std = [
 	"timestamp/std",
 	"parity-wasm/std",
 	"pwasm-utils/std",
+	"wasmi-validation/std",
 ]
diff --git a/substrate/srml/contract/src/wasm/code_cache.rs b/substrate/srml/contract/src/wasm/code_cache.rs
index dab8c4bfa4b..0c71fe8cb5b 100644
--- a/substrate/srml/contract/src/wasm/code_cache.rs
+++ b/substrate/srml/contract/src/wasm/code_cache.rs
@@ -72,8 +72,6 @@ pub fn save<T: Trait>(
 	let prefab_module = prepare::prepare_contract::<T, Env>(&original_code, schedule)?;
 	let code_hash = T::Hashing::hash(&original_code);
 
-	// TODO: #1416 validate the code. If the code is not valid, then don't store it.
-
 	<CodeStorage<T>>::insert(code_hash, prefab_module);
 	<PristineCode<T>>::insert(code_hash, original_code);
 
diff --git a/substrate/srml/contract/src/wasm/prepare.rs b/substrate/srml/contract/src/wasm/prepare.rs
index 15922c3d999..53883451b42 100644
--- a/substrate/srml/contract/src/wasm/prepare.rs
+++ b/substrate/srml/contract/src/wasm/prepare.rs
@@ -29,20 +29,34 @@ use rstd::prelude::*;
 use runtime_primitives::traits::As;
 
 struct ContractModule<'a, Gas: 'a> {
-	// An `Option` is used here for loaning (`take()`-ing) the module.
-	// Invariant: Can't be `None` (i.e. on enter and on exit from the function
-	// the value *must* be `Some`).
+	/// A deserialized module. The module is valid (this is Guaranteed by `new` method).
+	///
+	/// An `Option` is used here for loaning (`take()`-ing) the module.
+	/// Invariant: Can't be `None` (i.e. on enter and on exit from the function
+	/// the value *must* be `Some`).
 	module: Option<elements::Module>,
 	schedule: &'a Schedule<Gas>,
 }
 
 impl<'a, Gas: 'a + As<u32> + Clone> ContractModule<'a, Gas> {
+	/// Creates a new instance of `ContractModule`.
+	///
+	/// Returns `Err` if the `original_code` couldn't be decoded or
+	/// if it contains an invalid module.
 	fn new(
 		original_code: &[u8],
 		schedule: &'a Schedule<Gas>,
 	) -> Result<ContractModule<'a, Gas>, &'static str> {
+		use wasmi_validation::{validate_module, PlainValidator};
+
 		let module =
-			elements::deserialize_buffer(original_code).map_err(|_| "can't decode wasm code")?;
+			elements::deserialize_buffer(original_code).map_err(|_| "Can't decode wasm code")?;
+
+		// Make sure that the module is valid.
+		validate_module::<PlainValidator>(&module).map_err(|_| "Module is not valid")?;
+
+		// Return a `ContractModule` instance with
+		// __valid__ module.
 		Ok(ContractModule {
 			module: Some(module),
 			schedule,
@@ -270,7 +284,8 @@ impl<'a, Gas: 'a + As<u32> + Clone> ContractModule<'a, Gas> {
 ///
 /// The checks are:
 ///
-/// - module doesn't define an internal memory instance,
+/// - provided code is a valid wasm module.
+/// - the module doesn't define an internal memory instance,
 /// - imported memory (if any) doesn't reserve more memory than permitted by the `schedule`,
 /// - all imported functions from the external environment matches defined by `env` module,
 ///
@@ -438,7 +453,7 @@ mod tests {
 				(func (export "deploy"))
 			)
 			"#,
-			Err("Requested initial number of pages should not exceed the requested maximum")
+			Err("Module is not valid")
 		);
 
 		prepare_test!(no_maximum,
@@ -487,7 +502,7 @@ mod tests {
 				(func (export "deploy"))
 			)
 			"#,
-			Err("Multiple memory imports defined")
+			Err("Module is not valid")
 		);
 
 		prepare_test!(table_import,
@@ -505,7 +520,7 @@ mod tests {
 		prepare_test!(global_import,
 			r#"
 			(module
-				(global $g (import "env" "global") (mut i32))
+				(global $g (import "env" "global") i32)
 				(func (export "call"))
 				(func (export "deploy"))
 			)
-- 
GitLab