Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/fuzzing/src/oracles/diff_wasmi.rs
1693 views
1
//! Evaluate an exported Wasm function using the wasmi interpreter.
2
3
use crate::generators::{Config, DiffValue, DiffValueType};
4
use crate::oracles::engine::{DiffEngine, DiffInstance};
5
use anyhow::{Context, Error, Result};
6
use wasmtime::Trap;
7
8
/// A wrapper for `wasmi` as a [`DiffEngine`].
9
pub struct WasmiEngine {
10
engine: wasmi::Engine,
11
}
12
13
impl WasmiEngine {
14
pub(crate) fn new(config: &mut Config) -> Self {
15
let config = &mut config.module_config.config;
16
// Force generated Wasm modules to never have features that Wasmi doesn't support.
17
config.relaxed_simd_enabled = false;
18
config.threads_enabled = false;
19
config.exceptions_enabled = false;
20
config.gc_enabled = false;
21
22
// FIXME: once the active fuzz bug for wasmi's simd differential fuzzing
23
// has been fixed and we've updated then this should be re-enabled.
24
config.simd_enabled = false;
25
// FIXME: requires updating to a wasmi that contains
26
// wasmi-labs/wasmi#1531.
27
config.memory64_enabled = false;
28
// FIXME: until https://github.com/wasmi-labs/wasmi/issues/1544 is fixed.
29
config.wide_arithmetic_enabled = false;
30
31
let mut wasmi_config = wasmi::Config::default();
32
wasmi_config
33
.consume_fuel(false)
34
.floats(true)
35
.wasm_mutable_global(true)
36
.wasm_sign_extension(config.sign_extension_ops_enabled)
37
.wasm_saturating_float_to_int(config.saturating_float_to_int_enabled)
38
.wasm_multi_value(config.multi_value_enabled)
39
.wasm_bulk_memory(config.bulk_memory_enabled)
40
.wasm_reference_types(config.reference_types_enabled)
41
.wasm_tail_call(config.tail_call_enabled)
42
.wasm_multi_memory(config.max_memories > 1)
43
.wasm_extended_const(config.extended_const_enabled)
44
.wasm_custom_page_sizes(config.custom_page_sizes_enabled)
45
.wasm_memory64(config.memory64_enabled)
46
.wasm_simd(config.simd_enabled)
47
.wasm_wide_arithmetic(config.wide_arithmetic_enabled);
48
Self {
49
engine: wasmi::Engine::new(&wasmi_config),
50
}
51
}
52
53
fn trap_code(&self, err: &Error) -> Option<wasmi::core::TrapCode> {
54
let err = err.downcast_ref::<wasmi::Error>()?;
55
if let Some(code) = err.as_trap_code() {
56
return Some(code);
57
}
58
59
match err.kind() {
60
wasmi::errors::ErrorKind::Instantiation(
61
wasmi::errors::InstantiationError::ElementSegmentDoesNotFit { .. },
62
) => Some(wasmi::core::TrapCode::TableOutOfBounds),
63
wasmi::errors::ErrorKind::Memory(wasmi::errors::MemoryError::OutOfBoundsAccess) => {
64
Some(wasmi::core::TrapCode::MemoryOutOfBounds)
65
}
66
_ => {
67
log::trace!("unknown wasmi error: {:?}", err.kind());
68
None
69
}
70
}
71
}
72
}
73
74
impl DiffEngine for WasmiEngine {
75
fn name(&self) -> &'static str {
76
"wasmi"
77
}
78
79
fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> {
80
let module =
81
wasmi::Module::new(&self.engine, wasm).context("unable to validate Wasm module")?;
82
let mut store = wasmi::Store::new(&self.engine, ());
83
let instance = wasmi::Linker::<()>::new(&self.engine)
84
.instantiate(&mut store, &module)
85
.and_then(|i| i.start(&mut store))
86
.context("unable to instantiate module in wasmi")?;
87
Ok(Box::new(WasmiInstance { store, instance }))
88
}
89
90
fn assert_error_match(&self, lhs: &Error, rhs: &Trap) {
91
match self.trap_code(lhs) {
92
Some(code) => assert_eq!(wasmi_to_wasmtime_trap_code(code), *rhs),
93
None => panic!("unexpected wasmi error {lhs:?}"),
94
}
95
}
96
97
fn is_non_deterministic_error(&self, err: &Error) -> bool {
98
matches!(
99
self.trap_code(err),
100
Some(wasmi::core::TrapCode::StackOverflow)
101
)
102
}
103
}
104
105
/// Converts `wasmi` trap code to `wasmtime` trap code.
106
fn wasmi_to_wasmtime_trap_code(trap: wasmi::core::TrapCode) -> Trap {
107
use wasmi::core::TrapCode;
108
match trap {
109
TrapCode::UnreachableCodeReached => Trap::UnreachableCodeReached,
110
TrapCode::MemoryOutOfBounds => Trap::MemoryOutOfBounds,
111
TrapCode::TableOutOfBounds => Trap::TableOutOfBounds,
112
TrapCode::IndirectCallToNull => Trap::IndirectCallToNull,
113
TrapCode::IntegerDivisionByZero => Trap::IntegerDivisionByZero,
114
TrapCode::IntegerOverflow => Trap::IntegerOverflow,
115
TrapCode::BadConversionToInteger => Trap::BadConversionToInteger,
116
TrapCode::StackOverflow => Trap::StackOverflow,
117
TrapCode::BadSignature => Trap::BadSignature,
118
TrapCode::OutOfFuel => unimplemented!("built-in fuel metering is unused"),
119
TrapCode::GrowthOperationLimited => unimplemented!("resource limiter is unused"),
120
}
121
}
122
123
/// A wrapper for `wasmi` Wasm instances.
124
struct WasmiInstance {
125
store: wasmi::Store<()>,
126
instance: wasmi::Instance,
127
}
128
129
impl DiffInstance for WasmiInstance {
130
fn name(&self) -> &'static str {
131
"wasmi"
132
}
133
134
fn evaluate(
135
&mut self,
136
function_name: &str,
137
arguments: &[DiffValue],
138
result_tys: &[DiffValueType],
139
) -> Result<Option<Vec<DiffValue>>> {
140
let function = self
141
.instance
142
.get_export(&self.store, function_name)
143
.and_then(wasmi::Extern::into_func)
144
.unwrap();
145
let arguments: Vec<_> = arguments.iter().map(|x| x.into()).collect();
146
let mut results = vec![wasmi::Val::I32(0); result_tys.len()];
147
function
148
.call(&mut self.store, &arguments, &mut results)
149
.context("wasmi function trap")?;
150
Ok(Some(results.into_iter().map(Into::into).collect()))
151
}
152
153
fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> {
154
Some(
155
self.instance
156
.get_export(&self.store, name)
157
.unwrap()
158
.into_global()
159
.unwrap()
160
.get(&self.store)
161
.into(),
162
)
163
}
164
165
fn get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>> {
166
assert!(!shared);
167
Some(
168
self.instance
169
.get_export(&self.store, name)
170
.unwrap()
171
.into_memory()
172
.unwrap()
173
.data(&self.store)
174
.to_vec(),
175
)
176
}
177
}
178
179
impl From<&DiffValue> for wasmi::Val {
180
fn from(v: &DiffValue) -> Self {
181
use wasmi::Val as WasmiValue;
182
match *v {
183
DiffValue::I32(n) => WasmiValue::I32(n),
184
DiffValue::I64(n) => WasmiValue::I64(n),
185
DiffValue::F32(n) => WasmiValue::F32(wasmi::core::F32::from_bits(n)),
186
DiffValue::F64(n) => WasmiValue::F64(wasmi::core::F64::from_bits(n)),
187
DiffValue::V128(n) => WasmiValue::V128(wasmi::core::V128::from(n)),
188
DiffValue::FuncRef { null } => {
189
assert!(null);
190
WasmiValue::FuncRef(wasmi::FuncRef::null())
191
}
192
DiffValue::ExternRef { null } => {
193
assert!(null);
194
WasmiValue::ExternRef(wasmi::ExternRef::null())
195
}
196
DiffValue::AnyRef { .. } => unimplemented!(),
197
DiffValue::ExnRef { .. } => unimplemented!(),
198
DiffValue::ContRef { .. } => unimplemented!(),
199
}
200
}
201
}
202
203
impl From<wasmi::Val> for DiffValue {
204
fn from(value: wasmi::Val) -> Self {
205
use wasmi::Val as WasmiValue;
206
match value {
207
WasmiValue::I32(n) => DiffValue::I32(n),
208
WasmiValue::I64(n) => DiffValue::I64(n),
209
WasmiValue::F32(n) => DiffValue::F32(n.to_bits()),
210
WasmiValue::F64(n) => DiffValue::F64(n.to_bits()),
211
WasmiValue::V128(n) => DiffValue::V128(n.as_u128()),
212
WasmiValue::FuncRef(f) => DiffValue::FuncRef { null: f.is_null() },
213
WasmiValue::ExternRef(e) => DiffValue::ExternRef { null: e.is_null() },
214
}
215
}
216
}
217
218
#[cfg(test)]
219
mod tests {
220
use super::*;
221
222
#[test]
223
fn smoke() {
224
crate::oracles::engine::smoke_test_engine(|_, config| Ok(WasmiEngine::new(config)))
225
}
226
}
227
228