xref: /wasmtime-44.0.1/fuzz/fuzz_targets/misc.rs (revision fee9be21)
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