Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/fuzzing/src/oracles/diff_wasmtime.rs
1693 views
1
//! Evaluate an exported Wasm function using Wasmtime.
2
3
use crate::generators::{self, CompilerStrategy, DiffValue, DiffValueType, WasmtimeConfig};
4
use crate::oracles::dummy;
5
use crate::oracles::engine::DiffInstance;
6
use crate::oracles::{StoreLimits, compile_module, engine::DiffEngine};
7
use crate::single_module_fuzzer::KnownValid;
8
use anyhow::{Context, Error, Result};
9
use arbitrary::Unstructured;
10
use wasmtime::{Extern, FuncType, Instance, Module, Store, Trap, Val};
11
12
/// A wrapper for using Wasmtime as a [`DiffEngine`].
13
pub struct WasmtimeEngine {
14
config: generators::Config,
15
}
16
17
impl WasmtimeEngine {
18
/// Merely store the configuration; the engine is actually constructed
19
/// later. Ideally the store and engine could be built here but
20
/// `compile_module` takes a [`generators::Config`]; TODO re-factor this if
21
/// that ever changes.
22
pub fn new(
23
u: &mut Unstructured<'_>,
24
config: &mut generators::Config,
25
compiler_strategy: CompilerStrategy,
26
) -> arbitrary::Result<Self> {
27
let mut new_config = u.arbitrary::<WasmtimeConfig>()?;
28
new_config.compiler_strategy = compiler_strategy;
29
new_config.update_module_config(&mut config.module_config, u)?;
30
new_config.make_compatible_with(&config.wasmtime);
31
32
let config = generators::Config {
33
wasmtime: new_config,
34
module_config: config.module_config.clone(),
35
};
36
log::debug!("Created new Wasmtime differential engine with config: {config:?}");
37
38
Ok(Self { config })
39
}
40
}
41
42
impl DiffEngine for WasmtimeEngine {
43
fn name(&self) -> &'static str {
44
match self.config.wasmtime.compiler_strategy {
45
CompilerStrategy::CraneliftNative => "wasmtime",
46
CompilerStrategy::Winch => "winch",
47
CompilerStrategy::CraneliftPulley => "pulley",
48
}
49
}
50
51
fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> {
52
let store = self.config.to_store();
53
let module = compile_module(store.engine(), wasm, KnownValid::Yes, &self.config).unwrap();
54
let instance = WasmtimeInstance::new(store, module)?;
55
Ok(Box::new(instance))
56
}
57
58
fn assert_error_match(&self, lhs: &Error, rhs: &Trap) {
59
let lhs = lhs
60
.downcast_ref::<Trap>()
61
.expect(&format!("not a trap: {lhs:?}"));
62
63
assert_eq!(lhs, rhs, "{lhs}\nis not equal to\n{rhs}");
64
}
65
66
fn is_non_deterministic_error(&self, err: &Error) -> bool {
67
match err.downcast_ref::<Trap>() {
68
Some(trap) => super::wasmtime_trap_is_non_deterministic(trap),
69
None => false,
70
}
71
}
72
}
73
74
/// A wrapper around a Wasmtime instance.
75
///
76
/// The Wasmtime engine constructs a new store and compiles an instance of a
77
/// Wasm module.
78
pub struct WasmtimeInstance {
79
store: Store<StoreLimits>,
80
instance: Instance,
81
}
82
83
impl WasmtimeInstance {
84
/// Instantiate a new Wasmtime instance.
85
pub fn new(mut store: Store<StoreLimits>, module: Module) -> Result<Self> {
86
let instance = dummy::dummy_linker(&mut store, &module)
87
.and_then(|l| l.instantiate(&mut store, &module))
88
.context("unable to instantiate module in wasmtime")?;
89
Ok(Self { store, instance })
90
}
91
92
/// Retrieve the names and types of all exported functions in the instance.
93
///
94
/// This is useful for evaluating each exported function with different
95
/// values. The [`DiffInstance`] trait asks for the function name and we
96
/// need to know the function signature in order to pass in the right
97
/// arguments.
98
pub fn exported_functions(&mut self) -> Vec<(String, FuncType)> {
99
let exported_functions = self
100
.instance
101
.exports(&mut self.store)
102
.map(|e| (e.name().to_owned(), e.into_func()))
103
.filter_map(|(n, f)| f.map(|f| (n, f)))
104
.collect::<Vec<_>>();
105
exported_functions
106
.into_iter()
107
.map(|(n, f)| (n, f.ty(&self.store)))
108
.collect()
109
}
110
111
/// Returns the list of globals and their types exported from this instance.
112
pub fn exported_globals(&mut self) -> Vec<(String, DiffValueType)> {
113
let globals = self
114
.instance
115
.exports(&mut self.store)
116
.filter_map(|e| {
117
let name = e.name();
118
e.into_global().map(|g| (name.to_string(), g))
119
})
120
.collect::<Vec<_>>();
121
122
globals
123
.into_iter()
124
.filter_map(|(name, global)| {
125
DiffValueType::try_from(global.ty(&self.store).content().clone())
126
.map(|ty| (name, ty))
127
.ok()
128
})
129
.collect()
130
}
131
132
/// Returns the list of exported memories and whether or not it's a shared
133
/// memory.
134
pub fn exported_memories(&mut self) -> Vec<(String, bool)> {
135
self.instance
136
.exports(&mut self.store)
137
.filter_map(|e| {
138
let name = e.name();
139
match e.into_extern() {
140
Extern::Memory(_) => Some((name.to_string(), false)),
141
Extern::SharedMemory(_) => Some((name.to_string(), true)),
142
_ => None,
143
}
144
})
145
.collect()
146
}
147
148
/// Returns whether or not this instance has hit its OOM condition yet.
149
pub fn is_oom(&self) -> bool {
150
self.store.data().is_oom()
151
}
152
}
153
154
impl DiffInstance for WasmtimeInstance {
155
fn name(&self) -> &'static str {
156
"wasmtime"
157
}
158
159
fn evaluate(
160
&mut self,
161
function_name: &str,
162
arguments: &[DiffValue],
163
_results: &[DiffValueType],
164
) -> Result<Option<Vec<DiffValue>>> {
165
let arguments: Vec<_> = arguments.iter().map(Val::from).collect();
166
167
let function = self
168
.instance
169
.get_func(&mut self.store, function_name)
170
.expect("unable to access exported function");
171
let ty = function.ty(&self.store);
172
let mut results = vec![Val::I32(0); ty.results().len()];
173
function.call(&mut self.store, &arguments, &mut results)?;
174
175
let results = results.into_iter().map(Val::into).collect();
176
Ok(Some(results))
177
}
178
179
fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> {
180
Some(
181
self.instance
182
.get_global(&mut self.store, name)
183
.unwrap()
184
.get(&mut self.store)
185
.into(),
186
)
187
}
188
189
fn get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>> {
190
Some(if shared {
191
let memory = self
192
.instance
193
.get_shared_memory(&mut self.store, name)
194
.unwrap();
195
memory.data().iter().map(|i| unsafe { *i.get() }).collect()
196
} else {
197
self.instance
198
.get_memory(&mut self.store, name)
199
.unwrap()
200
.data(&self.store)
201
.to_vec()
202
})
203
}
204
}
205
206
impl From<&DiffValue> for Val {
207
fn from(v: &DiffValue) -> Self {
208
match *v {
209
DiffValue::I32(n) => Val::I32(n),
210
DiffValue::I64(n) => Val::I64(n),
211
DiffValue::F32(n) => Val::F32(n),
212
DiffValue::F64(n) => Val::F64(n),
213
DiffValue::V128(n) => Val::V128(n.into()),
214
DiffValue::FuncRef { null } => {
215
assert!(null);
216
Val::FuncRef(None)
217
}
218
DiffValue::ExternRef { null } => {
219
assert!(null);
220
Val::ExternRef(None)
221
}
222
DiffValue::AnyRef { null } => {
223
assert!(null);
224
Val::AnyRef(None)
225
}
226
DiffValue::ExnRef { null } => {
227
assert!(null);
228
Val::ExnRef(None)
229
}
230
DiffValue::ContRef { null } => {
231
assert!(null);
232
Val::ExnRef(None)
233
}
234
}
235
}
236
}
237
238
impl From<Val> for DiffValue {
239
fn from(val: Val) -> DiffValue {
240
match val {
241
Val::I32(n) => DiffValue::I32(n),
242
Val::I64(n) => DiffValue::I64(n),
243
Val::F32(n) => DiffValue::F32(n),
244
Val::F64(n) => DiffValue::F64(n),
245
Val::V128(n) => DiffValue::V128(n.into()),
246
Val::ExternRef(r) => DiffValue::ExternRef { null: r.is_none() },
247
Val::FuncRef(r) => DiffValue::FuncRef { null: r.is_none() },
248
Val::AnyRef(r) => DiffValue::AnyRef { null: r.is_none() },
249
Val::ExnRef(e) => DiffValue::ExnRef { null: e.is_none() },
250
Val::ContRef(c) => DiffValue::ContRef { null: c.is_none() },
251
}
252
}
253
}
254
255
#[cfg(test)]
256
mod tests {
257
use super::*;
258
259
#[test]
260
fn smoke_cranelift_native() {
261
crate::oracles::engine::smoke_test_engine(|u, config| {
262
WasmtimeEngine::new(u, config, CompilerStrategy::CraneliftNative)
263
})
264
}
265
266
#[test]
267
fn smoke_cranelift_pulley() {
268
crate::oracles::engine::smoke_test_engine(|u, config| {
269
WasmtimeEngine::new(u, config, CompilerStrategy::CraneliftPulley)
270
})
271
}
272
273
#[test]
274
fn smoke_winch() {
275
if !cfg!(target_arch = "x86_64") {
276
return;
277
}
278
crate::oracles::engine::smoke_test_engine(|u, config| {
279
WasmtimeEngine::new(u, config, CompilerStrategy::Winch)
280
})
281
}
282
}
283
284