Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/fuzz/fuzz_targets/misc.rs
3067 views
1
#![no_main]
2
3
use libfuzzer_sys::arbitrary::{Arbitrary, Result, Unstructured};
4
use libfuzzer_sys::fuzz_target;
5
use std::sync::OnceLock;
6
7
// Helper macro which takes a static list of fuzzers as input which are then
8
// delegated to internally based on the fuzz target selected.
9
//
10
// In general this fuzz target will execute a number of fuzzers all with the
11
// same input. The `FUZZER` environment variable can be used to forcibly disable
12
// all but one.
13
macro_rules! run_fuzzers {
14
($($fuzzer:ident)*) => {
15
static ENABLED: OnceLock<u32> = OnceLock::new();
16
17
fuzz_target!(
18
init: wasmtime_fuzzing::misc_init(),
19
|bytes: &[u8]| {
20
// Use the first byte of input as a discriminant of which fuzzer to
21
// select.
22
let Some((which_fuzzer, bytes)) = bytes.split_first() else {
23
return;
24
};
25
26
// Lazily initialize this fuzzer in terms of logging as well as
27
// enabled fuzzers via the `FUZZER` env var. This creates a bitmask
28
// inside of `ENABLED` of enabled fuzzers, returned here as
29
// `enabled`.
30
let enabled = *ENABLED.get_or_init(|| {
31
let configured = std::env::var("FUZZER").ok();
32
let configured = configured.as_deref();
33
let mut enabled = 0;
34
let mut index = 0;
35
36
$(
37
if configured.is_none() || configured == Some(stringify!($fuzzer)) {
38
enabled |= 1 << index;
39
}
40
index += 1;
41
)*
42
let _ = index;
43
44
enabled
45
});
46
47
// Generate a linear check for each fuzzer. Only run each fuzzer if
48
// the fuzzer is enabled, and also only if the `which_fuzzer`
49
// discriminant matches the fuzzer being run.
50
//
51
// Note that it's a bit wonky here due to rust macros.
52
let mut index = 0;
53
$(
54
if enabled & (1 << index) != 0 && *which_fuzzer == index {
55
let _: Result<()> = $fuzzer(Unstructured::new(bytes));
56
}
57
index += 1;
58
)*
59
let _ = index;
60
});
61
};
62
}
63
64
run_fuzzers! {
65
pulley_roundtrip
66
assembler_roundtrip
67
memory_accesses
68
stacks
69
api_calls
70
dominator_tree
71
component_async
72
}
73
74
fn pulley_roundtrip(u: Unstructured<'_>) -> Result<()> {
75
pulley_interpreter_fuzz::roundtrip(Arbitrary::arbitrary_take_rest(u)?);
76
Ok(())
77
}
78
79
fn assembler_roundtrip(u: Unstructured<'_>) -> Result<()> {
80
use cranelift_assembler_x64::{Inst, fuzz};
81
let inst: Inst<fuzz::FuzzRegs> = Arbitrary::arbitrary_take_rest(u)?;
82
fuzz::roundtrip(&inst);
83
Ok(())
84
}
85
86
fn memory_accesses(u: Unstructured<'_>) -> Result<()> {
87
wasmtime_fuzzing::oracles::memory::check_memory_accesses(Arbitrary::arbitrary_take_rest(u)?);
88
Ok(())
89
}
90
91
fn stacks(u: Unstructured<'_>) -> Result<()> {
92
wasmtime_fuzzing::oracles::check_stacks(Arbitrary::arbitrary_take_rest(u)?);
93
Ok(())
94
}
95
96
fn api_calls(u: Unstructured<'_>) -> Result<()> {
97
wasmtime_fuzzing::oracles::make_api_calls(Arbitrary::arbitrary_take_rest(u)?);
98
Ok(())
99
}
100
101
fn dominator_tree(mut data: Unstructured<'_>) -> Result<()> {
102
use cranelift_codegen::cursor::{Cursor, FuncCursor};
103
use cranelift_codegen::dominator_tree::{DominatorTree, SimpleDominatorTree};
104
use cranelift_codegen::flowgraph::ControlFlowGraph;
105
use cranelift_codegen::ir::{
106
Block, BlockCall, Function, InstBuilder, JumpTableData, Value, types::I32,
107
};
108
use std::collections::HashMap;
109
110
const MAX_BLOCKS: u16 = 1 << 12;
111
112
let mut func = Function::new();
113
114
let mut num_to_block = Vec::new();
115
116
let mut cfg = HashMap::<Block, Vec<Block>>::new();
117
118
for edge in data.arbitrary_iter::<(u16, u16)>()? {
119
let (a, b) = edge?;
120
121
let a = a % MAX_BLOCKS;
122
let b = b % MAX_BLOCKS;
123
124
while a >= num_to_block.len() as u16 {
125
num_to_block.push(func.dfg.make_block());
126
}
127
128
let a = num_to_block[a as usize];
129
130
while b >= num_to_block.len() as u16 {
131
num_to_block.push(func.dfg.make_block());
132
}
133
134
let b = num_to_block[b as usize];
135
136
cfg.entry(a).or_default().push(b);
137
}
138
139
let mut cursor = FuncCursor::new(&mut func);
140
141
let mut v0: Option<Value> = None;
142
143
for block in num_to_block {
144
cursor.insert_block(block);
145
146
if v0.is_none() {
147
v0 = Some(cursor.ins().iconst(I32, 0));
148
}
149
150
if let Some(children) = cfg.get(&block) {
151
if children.len() == 1 {
152
cursor.ins().jump(children[0], &[]);
153
} else {
154
let block_calls = children
155
.iter()
156
.map(|&block| {
157
BlockCall::new(block, core::iter::empty(), &mut cursor.func.dfg.value_lists)
158
})
159
.collect::<Vec<_>>();
160
161
let data = JumpTableData::new(block_calls[0], &block_calls[1..]);
162
let jt = cursor.func.create_jump_table(data);
163
cursor.ins().br_table(v0.unwrap(), jt);
164
}
165
} else {
166
cursor.ins().return_(&[]);
167
}
168
}
169
170
let cfg = ControlFlowGraph::with_function(&func);
171
let domtree = DominatorTree::with_function(&func, &cfg);
172
let expected_domtree = SimpleDominatorTree::with_function(&func, &cfg);
173
174
for block in func.layout.blocks() {
175
let expected = expected_domtree.idom(block);
176
let got = domtree.idom(block);
177
if expected != got {
178
panic!("Expected dominator for {block} is {expected:?}, got {got:?}");
179
}
180
}
181
182
Ok(())
183
}
184
185
fn component_async(u: Unstructured<'_>) -> Result<()> {
186
wasmtime_fuzzing::oracles::component_async::run(Arbitrary::arbitrary_take_rest(u)?);
187
Ok(())
188
}
189
190