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
pulley_roundtrip(u: Unstructured<'_>) -> Result<()>74 fn pulley_roundtrip(u: Unstructured<'_>) -> Result<()> {
75 pulley_interpreter_fuzz::roundtrip(Arbitrary::arbitrary_take_rest(u)?);
76 Ok(())
77 }
78
assembler_roundtrip(u: Unstructured<'_>) -> Result<()>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
memory_accesses(u: Unstructured<'_>) -> Result<()>86 fn memory_accesses(u: Unstructured<'_>) -> Result<()> {
87 wasmtime_fuzzing::oracles::memory::check_memory_accesses(Arbitrary::arbitrary_take_rest(u)?);
88 Ok(())
89 }
90
stacks(u: Unstructured<'_>) -> Result<()>91 fn stacks(u: Unstructured<'_>) -> Result<()> {
92 wasmtime_fuzzing::oracles::check_stacks(Arbitrary::arbitrary_take_rest(u)?);
93 Ok(())
94 }
95
api_calls(u: Unstructured<'_>) -> Result<()>96 fn api_calls(u: Unstructured<'_>) -> Result<()> {
97 wasmtime_fuzzing::oracles::make_api_calls(Arbitrary::arbitrary_take_rest(u)?);
98 Ok(())
99 }
100
dominator_tree(mut data: Unstructured<'_>) -> Result<()>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
component_async(u: Unstructured<'_>) -> Result<()>185 fn component_async(u: Unstructured<'_>) -> Result<()> {
186 wasmtime_fuzzing::oracles::component_async::run(Arbitrary::arbitrary_take_rest(u)?);
187 Ok(())
188 }
189