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