contracts: Validate code before deployment (#2330)

* Validate module before storing it in code_cache.

* Bump version.
This commit is contained in:
Sergei Pepyakin
2019-04-19 19:35:11 +02:00
committed by DemiMarie-parity
parent 2a463a7b2a
commit 07268022cc
8 changed files with 57 additions and 12 deletions
+11
View File
@@ -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"
+3
View File
@@ -41,6 +41,9 @@ consensus_authorities = { package = "substrate-consensus-authorities", path = ".
[features]
default = ["std"]
core = [
"contract/core",
]
std = [
"parity-codec/std",
"substrate-primitives/std",
+1 -1
View File
@@ -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,
};
+10
View File
@@ -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"
+4 -1
View File
@@ -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",
]
+5
View File
@@ -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",
]
@@ -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);
+23 -8
View File
@@ -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"))
)