diff --git a/packages/vm/src/compatibility.rs b/packages/vm/src/compatibility.rs index e354a10731..1d9bb6cc0b 100644 --- a/packages/vm/src/compatibility.rs +++ b/packages/vm/src/compatibility.rs @@ -78,12 +78,23 @@ const TABLE_SIZE_LIMIT: u32 = 2500; // entries /// when a user accidentally includes wasm-bindgen, they get a bunch of unsupported imports. const MAX_IMPORTS: usize = 100; +/// The maximum number of functions a contract can have. +/// Any contract with more functions than this will be rejected during static validation. const MAX_FUNCTIONS: usize = 20_000; +/// The maximum number of parameters a wasm function can have. +/// Any contract with a function type with more parameters than this will be rejected +/// during static validation. const MAX_FUNCTION_PARAMS: usize = 100; +/// The maximum total number of parameters of all functions in the wasm. +/// For each function in the wasm, take the number of parameters and sum all of these up. +/// If that sum exceeds this limit, the wasm will be rejected during static validation. const MAX_TOTAL_FUNCTION_PARAMS: usize = 10_000; +/// The maximum number of results a wasm function can have. +/// Any contract with a function type with more results than this will be rejected +/// during static validation. const MAX_FUNCTION_RESULTS: usize = 1; /// Checks if the data is valid wasm and compatibility with the CosmWasm API (imports and exports) @@ -935,4 +946,63 @@ mod tests { _ => panic!("Got unexpected error"), } } + + #[test] + fn check_wasm_fails_for_big_functions() { + // too many arguments + let args = " i32".repeat(MAX_FUNCTION_PARAMS + 1); + let wasm = wat::parse_str(format!( + r#"(module + (type (func (param {args}))) + (func (type 0) nop) + )"# + )) + .unwrap(); + let module = ParsedWasm::parse(&wasm).unwrap(); + + match check_wasm_functions(&module).unwrap_err() { + VmError::StaticValidationErr { msg, .. } => assert_eq!( + msg, + "Wasm contract contains function with more than 100 parameters" + ), + _ => panic!("Got unexpected error"), + } + + // too many returns + let return_types = " i32".repeat(MAX_FUNCTION_RESULTS + 1); + let returns = " i32.const 42".repeat(MAX_FUNCTION_RESULTS + 1); + let wasm = wat::parse_str(format!( + r#"(module + (type (func (result {return_types}))) + (func (type 0) {returns}) + )"# + )) + .unwrap(); + let module = ParsedWasm::parse(&wasm).unwrap(); + match check_wasm_functions(&module).unwrap_err() { + VmError::StaticValidationErr { msg, .. } => assert_eq!( + msg, + "Wasm contract contains function with more than 1 results" + ), + _ => panic!("Got unexpected error"), + } + + // too many functions + let functions = ["(func (type 0) nop)"; MAX_FUNCTIONS + 1]; + let functions = functions.join("\n"); + let wasm = wat::parse_str(format!( + r#"(module + (type (func)) + {functions} + )"# + )) + .unwrap(); + let module = ParsedWasm::parse(&wasm).unwrap(); + match check_wasm_functions(&module).unwrap_err() { + VmError::StaticValidationErr { msg, .. } => { + assert_eq!(msg, "Wasm contract contains more than 20000 functions") + } + _ => panic!("Got unexpected error"), + } + } } diff --git a/packages/vm/src/parsed_wasm.rs b/packages/vm/src/parsed_wasm.rs index c1afbde861..11c67210d1 100644 --- a/packages/vm/src/parsed_wasm.rs +++ b/packages/vm/src/parsed_wasm.rs @@ -249,4 +249,45 @@ mod test { .unwrap(); assert!(ParsedWasm::parse(&wasm_data).is_err()); } + + #[test] + fn parsed_wasm_counts_functions_correctly() { + let wasm = wat::parse_str(r#"(module)"#).unwrap(); + let module = ParsedWasm::parse(&wasm).unwrap(); + assert_eq!(module.function_count, 0); + + let wasm = wat::parse_str( + r#"(module + (type (func)) + (func (type 0) nop) + (func (type 0) nop) + (export "foo" (func 0)) + (export "bar" (func 0)) + )"#, + ) + .unwrap(); + let module = ParsedWasm::parse(&wasm).unwrap(); + assert_eq!(module.function_count, 2); + } + + #[test] + fn parsed_wasm_counts_func_io_correctly() { + let wasm = wat::parse_str(r#"(module)"#).unwrap(); + let module = ParsedWasm::parse(&wasm).unwrap(); + assert_eq!(module.max_func_params, 0); + assert_eq!(module.max_func_results, 0); + + let wasm = wat::parse_str( + r#"(module + (type (func (param i32 i32 i32) (result i32))) + (type (func (param i32) (result i32 i32))) + (func (type 1) i32.const 42 i32.const 42) + (func (type 0) i32.const 42) + )"#, + ) + .unwrap(); + let module = ParsedWasm::parse(&wasm).unwrap(); + assert_eq!(module.max_func_params, 3); + assert_eq!(module.max_func_results, 2); + } }