Skip to content

Commit 82dc2af

Browse files
committed
Refactor wasm validation
1 parent 9ac8ac1 commit 82dc2af

File tree

2 files changed

+54
-30
lines changed

2 files changed

+54
-30
lines changed

packages/vm/src/compatibility.rs

+5-11
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@ use wasmer::wasmparser::Export;
55
use wasmer::wasmparser::Import;
66
use wasmer::wasmparser::ImportSectionReader;
77
use wasmer::wasmparser::MemorySectionReader;
8-
use wasmer::wasmparser::Parser;
98
use wasmer::wasmparser::Payload;
109
use wasmer::wasmparser::TableSectionReader;
1110
use wasmer::wasmparser::TypeRef;
12-
use wasmer::wasmparser::Validator;
1311

1412
use crate::capabilities::required_capabilities_from_module;
1513
use crate::errors::{VmError, VmResult};
1614
use crate::limited::LimitedDisplay;
15+
use crate::static_analysis::validate_wasm;
1716
use crate::static_analysis::ExportInfo;
1817

1918
/// Lists all imports we provide upon instantiating the instance in Instance::from_module()
@@ -81,15 +80,8 @@ const MAX_IMPORTS: u32 = 100;
8180

8281
/// Checks if the data is valid wasm and compatibility with the CosmWasm API (imports and exports)
8382
pub fn check_wasm(wasm_code: &[u8], available_capabilities: &HashSet<String>) -> VmResult<()> {
84-
let parsed = Parser::new(0).parse_all(wasm_code);
85-
let mut validator = Validator::new();
86-
// TODO: some of the validator checks are duplicated in our checks below
87-
8883
let mut memory_section: Option<MemorySectionReader<'_>> = None;
89-
for payload in parsed {
90-
let payload = payload?;
91-
validator.payload(&payload)?;
92-
84+
validate_wasm(wasm_code, |payload| {
9385
match payload {
9486
Payload::TableSection(t) => check_wasm_tables(t)?,
9587
Payload::MemorySection(m) => memory_section = Some(m),
@@ -102,7 +94,9 @@ pub fn check_wasm(wasm_code: &[u8], available_capabilities: &HashSet<String>) ->
10294
Payload::ImportSection(i) => check_wasm_imports(i, SUPPORTED_IMPORTS)?,
10395
_ => {}
10496
}
105-
}
97+
Ok(())
98+
})?;
99+
// we want to fail if there is no memory section, so this check is delayed until the end
106100
check_wasm_memories(memory_section)?;
107101

108102
Ok(())

packages/vm/src/static_analysis.rs

+49-19
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use std::collections::HashSet;
22

3-
use wasmer::wasmparser::{Export, ExportSectionReader, ExternalKind};
3+
use wasmer::wasmparser::{
4+
Export, ExportSectionReader, ExternalKind, Parser, Payload, ValidPayload, Validator,
5+
WasmFeatures,
6+
};
47

58
use crate::errors::VmResult;
69

@@ -13,29 +16,56 @@ pub const REQUIRED_IBC_EXPORTS: &[&str] = &[
1316
"ibc_packet_timeout",
1417
];
1518

19+
/// Validates the given wasm code and calls the callback for each payload.
20+
/// "Validates" in this case refers to general WebAssembly validation, not specific to CosmWasm.
21+
pub fn validate_wasm<'a>(
22+
wasm_code: &'a [u8],
23+
mut handle_payload: impl FnMut(Payload<'a>) -> VmResult<()>,
24+
) -> VmResult<()> {
25+
let mut validator = Validator::new_with_features(WasmFeatures {
26+
mutable_global: false,
27+
saturating_float_to_int: false,
28+
sign_extension: true,
29+
reference_types: true,
30+
multi_value: false,
31+
bulk_memory: false,
32+
component_model: false,
33+
simd: false,
34+
relaxed_simd: false,
35+
threads: false,
36+
tail_call: false,
37+
deterministic_only: true,
38+
multi_memory: false,
39+
exceptions: false,
40+
memory64: false,
41+
extended_const: false,
42+
});
43+
44+
for p in Parser::new(0).parse_all(wasm_code) {
45+
let p = p?;
46+
// validate the payload
47+
if let ValidPayload::Func(fv, body) = validator.payload(&p)? {
48+
// also validate function bodies
49+
fv.into_validator(Default::default()).validate(&body)?;
50+
}
51+
// tell caller about the payload
52+
handle_payload(p)?;
53+
}
54+
55+
Ok(())
56+
}
57+
1658
/// A small helper macro to validate the wasm module and extract a reader for a specific section.
1759
macro_rules! extract_reader {
1860
($wasm_code: expr, $payload: ident, $t: ty) => {{
19-
fn extract(wasm_code: &[u8]) -> crate::VmResult<Option<$t>> {
20-
use wasmer::wasmparser::{Parser, Payload, ValidPayload, Validator};
21-
22-
let mut validator = Validator::new();
23-
let parser = Parser::new(0);
24-
61+
fn extract(wasm_code: &[u8]) -> $crate::VmResult<Option<$t>> {
2562
let mut value = None;
26-
for p in parser.parse_all(wasm_code) {
27-
let p = p?;
28-
// validate the payload
29-
if let ValidPayload::Func(mut fv, body) = validator.payload(&p)? {
30-
// also validate function bodies
31-
fv.validate(&body)?;
32-
}
33-
if let Payload::$payload(e) = p {
34-
// do not return immediately, as we want to validate the entire module
35-
value = Some(e);
63+
$crate::static_analysis::validate_wasm(wasm_code, |p| {
64+
if let Payload::$payload(p) = p {
65+
value = Some(p);
3666
}
37-
}
38-
67+
Ok(())
68+
})?;
3969
Ok(value)
4070
}
4171

0 commit comments

Comments
 (0)