Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve check_wasm_imports implementation #1629

Merged
merged 4 commits into from
Mar 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions contracts/burner/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions contracts/crypto-verify/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions contracts/cyberpunk/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions contracts/floaty/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions contracts/hackatom/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions contracts/ibc-reflect-send/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions contracts/ibc-reflect/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions contracts/queue/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions contracts/reflect/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions contracts/staking/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions contracts/virus/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
142 changes: 137 additions & 5 deletions packages/vm/src/compatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>) -> VmResult<()> {
let module = deserialize_wasm(wasm_code)?;
Expand Down Expand Up @@ -191,15 +197,23 @@ 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<ImportEntry> = module
let required_imports: &[ImportEntry] = module
.import_section()
.map_or(vec![], |import_section| import_section.entries().to_vec());
let required_import_names: BTreeSet<_> =
required_imports.iter().map(full_import_name).collect();
.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);
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
Expand Down Expand Up @@ -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(
Expand Down