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