From 20dd2c4d028878077face190780d405c033ab61f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 13 Mar 2023 15:53:08 +0100 Subject: [PATCH 1/4] Remove unnecessary vector clone --- packages/vm/src/compatibility.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vm/src/compatibility.rs b/packages/vm/src/compatibility.rs index 20680a450c..af797a3645 100644 --- a/packages/vm/src/compatibility.rs +++ b/packages/vm/src/compatibility.rs @@ -191,14 +191,14 @@ fn check_wasm_exports(module: &Module) -> VmResult<()> { /// When this is not the case, we either have an incompatibility between contract and VM /// or a error in the contract. fn check_wasm_imports(module: &Module, supported_imports: &[&str]) -> VmResult<()> { - let required_imports: Vec = module + let required_imports: &[ImportEntry] = module .import_section() - .map_or(vec![], |import_section| import_section.entries().to_vec()); + .map_or(&[], |import_section| import_section.entries()); let required_import_names: BTreeSet<_> = required_imports.iter().map(full_import_name).collect(); for required_import in required_imports { - let full_name = full_import_name(&required_import); + let full_name = full_import_name(required_import); if !supported_imports.contains(&full_name.as_str()) { return Err(VmError::static_validation_err(format!( "Wasm contract requires unsupported import: \"{}\". Required imports: {}. Available imports: {:?}.", From 9a3ec6438151485b4111771a9dc60d58fac529ed Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 13 Mar 2023 16:24:16 +0100 Subject: [PATCH 2/4] Only create BTreeSet of names for the error message --- packages/vm/src/compatibility.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vm/src/compatibility.rs b/packages/vm/src/compatibility.rs index af797a3645..59b3680833 100644 --- a/packages/vm/src/compatibility.rs +++ b/packages/vm/src/compatibility.rs @@ -194,12 +194,12 @@ fn check_wasm_imports(module: &Module, supported_imports: &[&str]) -> VmResult<( let required_imports: &[ImportEntry] = module .import_section() .map_or(&[], |import_section| import_section.entries()); - let required_import_names: BTreeSet<_> = - required_imports.iter().map(full_import_name).collect(); for required_import in required_imports { let full_name = full_import_name(required_import); if !supported_imports.contains(&full_name.as_str()) { + let required_import_names: BTreeSet<_> = + required_imports.iter().map(full_import_name).collect(); return Err(VmError::static_validation_err(format!( "Wasm contract requires unsupported import: \"{}\". Required imports: {}. Available imports: {:?}.", full_name, required_import_names.to_string_limited(200), supported_imports From 1fc64c7b4275b08d4e36aa9b97960250db149330 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 27 Mar 2023 10:55:54 +0200 Subject: [PATCH 3/4] Limit number of imports during static validation --- CHANGELOG.md | 2 + packages/vm/src/compatibility.rs | 132 +++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b95cf4e85..4e032aaaaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,9 @@ and this project adheres to ### Changed - cosmwasm-vm: Add checks for table section of Wasm blob ([#1631]). +- cosmwasm-vm: Limit number of imports during static validation ([#1629]). +[#1629]: https://github.com/CosmWasm/cosmwasm/pull/1629 [#1631]: https://github.com/CosmWasm/cosmwasm/pull/1631 ## [1.2.3] - 2023-03-22 diff --git a/packages/vm/src/compatibility.rs b/packages/vm/src/compatibility.rs index 59b3680833..428d51554f 100644 --- a/packages/vm/src/compatibility.rs +++ b/packages/vm/src/compatibility.rs @@ -64,6 +64,12 @@ const MEMORY_LIMIT: u32 = 512; // in pages /// is 485. Most are between 100 and 300. const TABLE_SIZE_LIMIT: u32 = 2500; // entries +/// If the contract has more than this amount of imports, it will be rejected +/// during static validation before even looking into the imports. We keep this +/// number high since failing early gives less detailed error messages. Especially +/// when a user accidentally includes wasm-bindgen, they get a bunch of unsupported imports. +const MAX_IMPORTS: usize = 100; + /// Checks if the data is valid wasm and compatibility with the CosmWasm API (imports and exports) pub fn check_wasm(wasm_code: &[u8], available_capabilities: &HashSet) -> VmResult<()> { let module = deserialize_wasm(wasm_code)?; @@ -195,6 +201,14 @@ fn check_wasm_imports(module: &Module, supported_imports: &[&str]) -> VmResult<( .import_section() .map_or(&[], |import_section| import_section.entries()); + if required_imports.len() > MAX_IMPORTS { + return Err(VmError::static_validation_err(format!( + "Import count exceeds limit. Imports: {}. Limit: {}.", + required_imports.len(), + MAX_IMPORTS + ))); + } + for required_import in required_imports { let full_name = full_import_name(required_import); if !supported_imports.contains(&full_name.as_str()) { @@ -640,6 +654,124 @@ mod tests { check_wasm_imports(&deserialize_wasm(&wasm).unwrap(), SUPPORTED_IMPORTS).unwrap(); } + #[test] + fn check_wasm_imports_exceeds_limit() { + let wasm = wat::parse_str( + r#"(module + (import "env" "db_write" (func (param i32 i32) (result i32))) + (import "env" "db_remove" (func (param i32) (result i32))) + (import "env" "addr_validate" (func (param i32) (result i32))) + (import "env" "addr_canonicalize" (func (param i32 i32) (result i32))) + (import "env" "addr_humanize" (func (param i32 i32) (result i32))) + (import "env" "secp256k1_verify" (func (param i32 i32 i32) (result i32))) + (import "env" "secp256k1_recover_pubkey" (func (param i32 i32 i32) (result i64))) + (import "env" "ed25519_verify" (func (param i32 i32 i32) (result i32))) + (import "env" "ed25519_batch_verify" (func (param i32 i32 i32) (result i32))) + (import "env" "spam01" (func (param i32 i32) (result i32))) + (import "env" "spam02" (func (param i32 i32) (result i32))) + (import "env" "spam03" (func (param i32 i32) (result i32))) + (import "env" "spam04" (func (param i32 i32) (result i32))) + (import "env" "spam05" (func (param i32 i32) (result i32))) + (import "env" "spam06" (func (param i32 i32) (result i32))) + (import "env" "spam07" (func (param i32 i32) (result i32))) + (import "env" "spam08" (func (param i32 i32) (result i32))) + (import "env" "spam09" (func (param i32 i32) (result i32))) + (import "env" "spam10" (func (param i32 i32) (result i32))) + (import "env" "spam11" (func (param i32 i32) (result i32))) + (import "env" "spam12" (func (param i32 i32) (result i32))) + (import "env" "spam13" (func (param i32 i32) (result i32))) + (import "env" "spam14" (func (param i32 i32) (result i32))) + (import "env" "spam15" (func (param i32 i32) (result i32))) + (import "env" "spam16" (func (param i32 i32) (result i32))) + (import "env" "spam17" (func (param i32 i32) (result i32))) + (import "env" "spam18" (func (param i32 i32) (result i32))) + (import "env" "spam19" (func (param i32 i32) (result i32))) + (import "env" "spam20" (func (param i32 i32) (result i32))) + (import "env" "spam21" (func (param i32 i32) (result i32))) + (import "env" "spam22" (func (param i32 i32) (result i32))) + (import "env" "spam23" (func (param i32 i32) (result i32))) + (import "env" "spam24" (func (param i32 i32) (result i32))) + (import "env" "spam25" (func (param i32 i32) (result i32))) + (import "env" "spam26" (func (param i32 i32) (result i32))) + (import "env" "spam27" (func (param i32 i32) (result i32))) + (import "env" "spam28" (func (param i32 i32) (result i32))) + (import "env" "spam29" (func (param i32 i32) (result i32))) + (import "env" "spam30" (func (param i32 i32) (result i32))) + (import "env" "spam31" (func (param i32 i32) (result i32))) + (import "env" "spam32" (func (param i32 i32) (result i32))) + (import "env" "spam33" (func (param i32 i32) (result i32))) + (import "env" "spam34" (func (param i32 i32) (result i32))) + (import "env" "spam35" (func (param i32 i32) (result i32))) + (import "env" "spam36" (func (param i32 i32) (result i32))) + (import "env" "spam37" (func (param i32 i32) (result i32))) + (import "env" "spam38" (func (param i32 i32) (result i32))) + (import "env" "spam39" (func (param i32 i32) (result i32))) + (import "env" "spam40" (func (param i32 i32) (result i32))) + (import "env" "spam41" (func (param i32 i32) (result i32))) + (import "env" "spam42" (func (param i32 i32) (result i32))) + (import "env" "spam43" (func (param i32 i32) (result i32))) + (import "env" "spam44" (func (param i32 i32) (result i32))) + (import "env" "spam45" (func (param i32 i32) (result i32))) + (import "env" "spam46" (func (param i32 i32) (result i32))) + (import "env" "spam47" (func (param i32 i32) (result i32))) + (import "env" "spam48" (func (param i32 i32) (result i32))) + (import "env" "spam49" (func (param i32 i32) (result i32))) + (import "env" "spam50" (func (param i32 i32) (result i32))) + (import "env" "spam51" (func (param i32 i32) (result i32))) + (import "env" "spam52" (func (param i32 i32) (result i32))) + (import "env" "spam53" (func (param i32 i32) (result i32))) + (import "env" "spam54" (func (param i32 i32) (result i32))) + (import "env" "spam55" (func (param i32 i32) (result i32))) + (import "env" "spam56" (func (param i32 i32) (result i32))) + (import "env" "spam57" (func (param i32 i32) (result i32))) + (import "env" "spam58" (func (param i32 i32) (result i32))) + (import "env" "spam59" (func (param i32 i32) (result i32))) + (import "env" "spam60" (func (param i32 i32) (result i32))) + (import "env" "spam61" (func (param i32 i32) (result i32))) + (import "env" "spam62" (func (param i32 i32) (result i32))) + (import "env" "spam63" (func (param i32 i32) (result i32))) + (import "env" "spam64" (func (param i32 i32) (result i32))) + (import "env" "spam65" (func (param i32 i32) (result i32))) + (import "env" "spam66" (func (param i32 i32) (result i32))) + (import "env" "spam67" (func (param i32 i32) (result i32))) + (import "env" "spam68" (func (param i32 i32) (result i32))) + (import "env" "spam69" (func (param i32 i32) (result i32))) + (import "env" "spam70" (func (param i32 i32) (result i32))) + (import "env" "spam71" (func (param i32 i32) (result i32))) + (import "env" "spam72" (func (param i32 i32) (result i32))) + (import "env" "spam73" (func (param i32 i32) (result i32))) + (import "env" "spam74" (func (param i32 i32) (result i32))) + (import "env" "spam75" (func (param i32 i32) (result i32))) + (import "env" "spam76" (func (param i32 i32) (result i32))) + (import "env" "spam77" (func (param i32 i32) (result i32))) + (import "env" "spam78" (func (param i32 i32) (result i32))) + (import "env" "spam79" (func (param i32 i32) (result i32))) + (import "env" "spam80" (func (param i32 i32) (result i32))) + (import "env" "spam81" (func (param i32 i32) (result i32))) + (import "env" "spam82" (func (param i32 i32) (result i32))) + (import "env" "spam83" (func (param i32 i32) (result i32))) + (import "env" "spam84" (func (param i32 i32) (result i32))) + (import "env" "spam85" (func (param i32 i32) (result i32))) + (import "env" "spam86" (func (param i32 i32) (result i32))) + (import "env" "spam87" (func (param i32 i32) (result i32))) + (import "env" "spam88" (func (param i32 i32) (result i32))) + (import "env" "spam89" (func (param i32 i32) (result i32))) + (import "env" "spam90" (func (param i32 i32) (result i32))) + (import "env" "spam91" (func (param i32 i32) (result i32))) + (import "env" "spam92" (func (param i32 i32) (result i32))) + )"#, + ) + .unwrap(); + let err = + check_wasm_imports(&deserialize_wasm(&wasm).unwrap(), SUPPORTED_IMPORTS).unwrap_err(); + match err { + VmError::StaticValidationErr { msg, .. } => { + assert_eq!(msg, "Import count exceeds limit. Imports: 101. Limit: 100."); + } + err => panic!("Unexpected error: {:?}", err), + } + } + #[test] fn check_wasm_imports_missing() { let wasm = wat::parse_str( From ade785e3a419e9bb56fac0184e62f55956dd8821 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 13 Mar 2023 17:12:25 +0100 Subject: [PATCH 4/4] Upgrade parity-wasm to 0.45 --- Cargo.lock | 4 ++-- contracts/burner/Cargo.lock | 4 ++-- contracts/crypto-verify/Cargo.lock | 4 ++-- contracts/cyberpunk/Cargo.lock | 4 ++-- contracts/floaty/Cargo.lock | 4 ++-- contracts/hackatom/Cargo.lock | 4 ++-- contracts/ibc-reflect-send/Cargo.lock | 4 ++-- contracts/ibc-reflect/Cargo.lock | 4 ++-- contracts/queue/Cargo.lock | 4 ++-- contracts/reflect/Cargo.lock | 4 ++-- contracts/staking/Cargo.lock | 4 ++-- contracts/virus/Cargo.lock | 4 ++-- packages/vm/Cargo.toml | 2 +- 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b50daa42a..0d4c9e75a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1173,9 +1173,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/burner/Cargo.lock b/contracts/burner/Cargo.lock index 1f34106c3c..09f43a75d1 100644 --- a/contracts/burner/Cargo.lock +++ b/contracts/burner/Cargo.lock @@ -949,9 +949,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/crypto-verify/Cargo.lock b/contracts/crypto-verify/Cargo.lock index 3a242b0ab1..c44004f0a2 100644 --- a/contracts/crypto-verify/Cargo.lock +++ b/contracts/crypto-verify/Cargo.lock @@ -981,9 +981,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/cyberpunk/Cargo.lock b/contracts/cyberpunk/Cargo.lock index 5d748dc697..ee82689005 100644 --- a/contracts/cyberpunk/Cargo.lock +++ b/contracts/cyberpunk/Cargo.lock @@ -987,9 +987,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/floaty/Cargo.lock b/contracts/floaty/Cargo.lock index e71c9a5f03..a217daf11f 100644 --- a/contracts/floaty/Cargo.lock +++ b/contracts/floaty/Cargo.lock @@ -959,9 +959,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/hackatom/Cargo.lock b/contracts/hackatom/Cargo.lock index ab8b12b3b4..f388ec77c5 100644 --- a/contracts/hackatom/Cargo.lock +++ b/contracts/hackatom/Cargo.lock @@ -960,9 +960,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/ibc-reflect-send/Cargo.lock b/contracts/ibc-reflect-send/Cargo.lock index 3f968aa2de..99eb548a5a 100644 --- a/contracts/ibc-reflect-send/Cargo.lock +++ b/contracts/ibc-reflect-send/Cargo.lock @@ -958,9 +958,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/ibc-reflect/Cargo.lock b/contracts/ibc-reflect/Cargo.lock index 051e88b9d8..d5c9576422 100644 --- a/contracts/ibc-reflect/Cargo.lock +++ b/contracts/ibc-reflect/Cargo.lock @@ -958,9 +958,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/queue/Cargo.lock b/contracts/queue/Cargo.lock index 2f440904e5..d01c16462f 100644 --- a/contracts/queue/Cargo.lock +++ b/contracts/queue/Cargo.lock @@ -938,9 +938,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/reflect/Cargo.lock b/contracts/reflect/Cargo.lock index 5531ebb18c..9ac802138a 100644 --- a/contracts/reflect/Cargo.lock +++ b/contracts/reflect/Cargo.lock @@ -946,9 +946,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/staking/Cargo.lock b/contracts/staking/Cargo.lock index a5f85c41a6..45626e9d59 100644 --- a/contracts/staking/Cargo.lock +++ b/contracts/staking/Cargo.lock @@ -952,9 +952,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/contracts/virus/Cargo.lock b/contracts/virus/Cargo.lock index e35f813893..ae89e3b9f9 100644 --- a/contracts/virus/Cargo.lock +++ b/contracts/virus/Cargo.lock @@ -938,9 +938,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "pin-project-lite" diff --git a/packages/vm/Cargo.toml b/packages/vm/Cargo.toml index b4b8981bbd..e15ed48528 100644 --- a/packages/vm/Cargo.toml +++ b/packages/vm/Cargo.toml @@ -44,7 +44,7 @@ clru = "0.4.0" cosmwasm-std = { path = "../std", version = "1.2.3", default-features = false } cosmwasm-crypto = { path = "../crypto", version = "1.2.3" } hex = "0.4" -parity-wasm = "0.42" +parity-wasm = "0.45" schemars = "0.8.3" serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"] } serde_json = "1.0.40"