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