diff --git a/.circleci/config.yml b/.circleci/config.yml index 336c8c38e..9f1c6641e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -731,7 +731,8 @@ jobs: contract_hackatom: docker: - - image: rust:1.81 + # We compile this contract with the upper bound to detect issues with new Rust versions early + - image: rust:1.84.1 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/hackatom @@ -743,9 +744,9 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_hackatom-rust:1.81-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_hackatom-rust:1.84.1-{{ checksum "Cargo.lock" }} - check_contract: - min_version: "2.2" + min_version: "3.0" - save_cache: paths: - /usr/local/cargo/registry @@ -755,7 +756,7 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_hackatom-rust:1.81-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_hackatom-rust:1.84.1-{{ checksum "Cargo.lock" }} contract_ibc_callbacks: docker: diff --git a/packages/vm/src/parsed_wasm.rs b/packages/vm/src/parsed_wasm.rs index c5d8c416f..bfbbff3bc 100644 --- a/packages/vm/src/parsed_wasm.rs +++ b/packages/vm/src/parsed_wasm.rs @@ -77,7 +77,8 @@ impl<'a> ParsedWasm<'a> { | WasmFeatures::SATURATING_FLOAT_TO_INT | WasmFeatures::SIGN_EXTENSION | WasmFeatures::MULTI_VALUE - | WasmFeatures::FLOATS; + | WasmFeatures::FLOATS + | WasmFeatures::REFERENCE_TYPES; let mut validator = Validator::new_with_features(features); diff --git a/packages/vm/src/wasm_backend/compile.rs b/packages/vm/src/wasm_backend/compile.rs index 6c85bfe4e..778601e73 100644 --- a/packages/vm/src/wasm_backend/compile.rs +++ b/packages/vm/src/wasm_backend/compile.rs @@ -20,4 +20,17 @@ mod tests { let engine = make_compiling_engine(None); assert!(compile(&engine, FLOATY).is_ok()); } + + #[test] + fn reference_types_dont_panic() { + const WASM: &str = r#"(module + (type $t0 (func (param funcref externref))) + (import "" "" (func $hello (type $t0))) + )"#; + + let wasm = wat::parse_str(WASM).unwrap(); + let engine = make_compiling_engine(None); + let error = compile(&engine, &wasm).unwrap_err(); + assert!(error.to_string().contains("FuncRef")); + } } diff --git a/packages/vm/src/wasm_backend/gatekeeper.rs b/packages/vm/src/wasm_backend/gatekeeper.rs index 573b53a9a..d7ddd4c8d 100644 --- a/packages/vm/src/wasm_backend/gatekeeper.rs +++ b/packages/vm/src/wasm_backend/gatekeeper.rs @@ -62,8 +62,12 @@ impl Default for Gatekeeper { fn default() -> Self { Self::new(GatekeeperConfig { allow_floats: true, - allow_feature_bulk_memory_operations: false, + // we allow the reference types proposal during compatibility checking because a subset + // of it is required since Rust 1.82, but we don't allow any of the instructions specific + // to the proposal here. Especially `table.grow` and `table.fill` can be abused to cause + // very long runtime and high memory usage. allow_feature_reference_types: false, + allow_feature_bulk_memory_operations: false, allow_feature_simd: false, allow_feature_exception_handling: false, allow_feature_threads: false, @@ -461,4 +465,52 @@ mod tests { .to_string() .contains("Bulk memory operation")); } + + #[test] + fn bulk_table_operations_not_supported() { + // these operations can take a long time with big tables + let deterministic = Arc::new(Gatekeeper::default()); + let mut compiler = make_compiler_config(); + compiler.push_middleware(deterministic); + let store = Store::new(compiler); + + let wasm = wat::parse_str( + r#" + (module + (table 2 funcref) + (func (export "test") (param $i i32) (result i32) + ;; grow table to size of $i + ref.null func + local.get $i + table.grow 0)) + "#, + ) + .unwrap(); + + let result = Module::new(&store, wasm); + assert!(result + .unwrap_err() + .to_string() + .contains("Reference type operation")); + + let wasm = wat::parse_str( + r#" + (module + (table 1000000000 funcref) + (func (export "test") (param $i i32) + ;; fill with nulls + i32.const 0 + ref.null func + i32.const 1000000000 + table.fill 0)) + "#, + ) + .unwrap(); + + let result = Module::new(&store, wasm); + assert!(result + .unwrap_err() + .to_string() + .contains("Reference type operation")); + } }