1 //! Generate Wasm modules that contain a single instruction.
2 
3 use super::ModuleConfig;
4 use arbitrary::Unstructured;
5 use wasm_encoder::{
6     CodeSection, ExportKind, ExportSection, Function, FunctionSection, Instruction, Module,
7     TypeSection, ValType,
8 };
9 
10 /// The name of the function generated by this module.
11 const FUNCTION_NAME: &'static str = "test";
12 
13 /// Configure a single instruction module.
14 ///
15 /// By explicitly defining the parameter and result types (versus generating the
16 /// module directly), we can more easily generate values of the right type.
17 #[derive(Clone)]
18 pub struct SingleInstModule<'a> {
19     instruction: Instruction<'a>,
20     parameters: &'a [ValType],
21     results: &'a [ValType],
22     feature: fn(&ModuleConfig) -> bool,
23 }
24 
25 impl<'a> SingleInstModule<'a> {
26     /// Choose a single-instruction module that matches `config`.
27     pub fn new(u: &mut Unstructured<'a>, config: &mut ModuleConfig) -> arbitrary::Result<&'a Self> {
28         // To avoid skipping modules unnecessarily during fuzzing, fix up the
29         // `ModuleConfig` to match the inherent limits of a single-instruction
30         // module.
31         config.config.min_funcs = 1;
32         config.config.max_funcs = 1;
33         config.config.min_tables = 0;
34         config.config.max_tables = 0;
35         config.config.min_memories = 0;
36         config.config.max_memories = 0;
37 
38         // Only select instructions that match the `ModuleConfig`.
39         let instructions = &INSTRUCTIONS
40             .iter()
41             .filter(|i| (i.feature)(config))
42             .collect::<Vec<_>>();
43         u.choose(&instructions[..]).copied()
44     }
45 
46     /// Encode a binary Wasm module with a single exported function, `test`,
47     /// that executes the single instruction.
48     pub fn to_bytes(&self) -> Vec<u8> {
49         let mut module = Module::new();
50 
51         // Encode the type section.
52         let mut types = TypeSection::new();
53         types.function(
54             self.parameters.iter().cloned(),
55             self.results.iter().cloned(),
56         );
57         module.section(&types);
58 
59         // Encode the function section.
60         let mut functions = FunctionSection::new();
61         let type_index = 0;
62         functions.function(type_index);
63         module.section(&functions);
64 
65         // Encode the export section.
66         let mut exports = ExportSection::new();
67         exports.export(FUNCTION_NAME, ExportKind::Func, 0);
68         module.section(&exports);
69 
70         // Encode the code section.
71         let mut codes = CodeSection::new();
72         let mut f = Function::new([]);
73         for (index, _) in self.parameters.iter().enumerate() {
74             f.instruction(&Instruction::LocalGet(index as u32));
75         }
76         f.instruction(&self.instruction);
77         f.instruction(&Instruction::End);
78         codes.function(&f);
79         module.section(&codes);
80 
81         // Extract the encoded Wasm bytes for this module.
82         module.finish()
83     }
84 }
85 
86 // MACROS
87 //
88 // These macros make it a bit easier to define the instructions available for
89 // generation. The idea is that, with these macros, we can define the list of
90 // instructions compactly and allow for easier changes to the Rust code (e.g.,
91 // `SingleInstModule`).
92 
93 macro_rules! valtype {
94     (i32) => {
95         ValType::I32
96     };
97     (i64) => {
98         ValType::I64
99     };
100     (f32) => {
101         ValType::F32
102     };
103     (f64) => {
104         ValType::F64
105     };
106 }
107 
108 macro_rules! binary {
109     ($inst:ident, $rust_ty:tt) => {
110         binary! { $inst, $rust_ty, $rust_ty }
111     };
112     ($inst:ident, $arguments_ty:tt,  $result_ty:tt) => {
113         SingleInstModule {
114             instruction: Instruction::$inst,
115             parameters: &[valtype!($arguments_ty), valtype!($arguments_ty)],
116             results: &[valtype!($result_ty)],
117             feature: |_| true,
118         }
119     };
120 }
121 
122 macro_rules! compare {
123     ($inst:ident, $rust_ty:tt) => {
124         binary! { $inst, $rust_ty, i32 }
125     };
126 }
127 
128 macro_rules! unary {
129     ($inst:ident, $rust_ty:tt) => {
130         unary! { $inst, $rust_ty, $rust_ty }
131     };
132     ($inst:ident, $argument_ty:tt, $result_ty:tt) => {
133         SingleInstModule {
134             instruction: Instruction::$inst,
135             parameters: &[valtype!($argument_ty)],
136             results: &[valtype!($result_ty)],
137             feature: |_| true,
138         }
139     };
140     ($inst:ident, $argument_ty:tt, $result_ty:tt, $feature:expr) => {
141         SingleInstModule {
142             instruction: Instruction::$inst,
143             parameters: &[valtype!($argument_ty)],
144             results: &[valtype!($result_ty)],
145             feature: $feature,
146         }
147     };
148 }
149 
150 macro_rules! convert {
151     ($inst:ident, $from_ty:tt -> $to_ty:tt) => {
152         unary! { $inst, $from_ty, $to_ty }
153     };
154     ($inst:ident, $from_ty:tt -> $to_ty:tt, $feature:expr) => {
155         unary! { $inst, $from_ty, $to_ty, $feature }
156     };
157 }
158 
159 static INSTRUCTIONS: &[SingleInstModule] = &[
160     // Integer arithmetic.
161     // I32Const
162     // I64Const
163     // F32Const
164     // F64Const
165     unary!(I32Clz, i32),
166     unary!(I64Clz, i64),
167     unary!(I32Ctz, i32),
168     unary!(I64Ctz, i64),
169     unary!(I32Popcnt, i32),
170     unary!(I64Popcnt, i64),
171     binary!(I32Add, i32),
172     binary!(I64Add, i64),
173     binary!(I32Sub, i32),
174     binary!(I64Sub, i64),
175     binary!(I32Mul, i32),
176     binary!(I64Mul, i64),
177     binary!(I32DivS, i32),
178     binary!(I64DivS, i64),
179     binary!(I32DivU, i32),
180     binary!(I64DivU, i64),
181     binary!(I32RemS, i32),
182     binary!(I64RemS, i64),
183     binary!(I32RemU, i32),
184     binary!(I64RemU, i64),
185     // Integer bitwise.
186     binary!(I32And, i32),
187     binary!(I64And, i64),
188     binary!(I32Or, i32),
189     binary!(I64Or, i64),
190     binary!(I32Xor, i32),
191     binary!(I64Xor, i64),
192     binary!(I32Shl, i32),
193     binary!(I64Shl, i64),
194     binary!(I32ShrS, i32),
195     binary!(I64ShrS, i64),
196     binary!(I32ShrU, i32),
197     binary!(I64ShrU, i64),
198     binary!(I32Rotl, i32),
199     binary!(I64Rotl, i64),
200     binary!(I32Rotr, i32),
201     binary!(I64Rotr, i64),
202     // Integer comparison.
203     unary!(I32Eqz, i32),
204     unary!(I64Eqz, i64, i32),
205     compare!(I32Eq, i32),
206     compare!(I64Eq, i64),
207     compare!(I32Ne, i32),
208     compare!(I64Ne, i64),
209     compare!(I32LtS, i32),
210     compare!(I64LtS, i64),
211     compare!(I32LtU, i32),
212     compare!(I64LtU, i64),
213     compare!(I32GtS, i32),
214     compare!(I64GtS, i64),
215     compare!(I32GtU, i32),
216     compare!(I64GtU, i64),
217     compare!(I32LeS, i32),
218     compare!(I64LeS, i64),
219     compare!(I32LeU, i32),
220     compare!(I64LeU, i64),
221     compare!(I32GeS, i32),
222     compare!(I64GeS, i64),
223     compare!(I32GeU, i32),
224     compare!(I64GeU, i64),
225     // Floating-point arithmetic.
226     unary!(F32Abs, f32),
227     unary!(F64Abs, f64),
228     unary!(F32Sqrt, f32),
229     unary!(F64Sqrt, f64),
230     unary!(F32Ceil, f32),
231     unary!(F64Ceil, f64),
232     unary!(F32Floor, f32),
233     unary!(F64Floor, f64),
234     unary!(F32Trunc, f32),
235     unary!(F64Trunc, f64),
236     unary!(F32Nearest, f32),
237     unary!(F64Nearest, f64),
238     unary!(F32Neg, f32),
239     unary!(F64Neg, f64),
240     binary!(F32Add, f32),
241     binary!(F64Add, f64),
242     binary!(F32Sub, f32),
243     binary!(F64Sub, f64),
244     binary!(F32Mul, f32),
245     binary!(F64Mul, f64),
246     binary!(F32Div, f32),
247     binary!(F64Div, f64),
248     binary!(F32Min, f32),
249     binary!(F64Min, f64),
250     binary!(F32Max, f32),
251     binary!(F64Max, f64),
252     binary!(F32Copysign, f32),
253     binary!(F64Copysign, f64),
254     // Floating-point comparison.
255     compare!(F32Eq, f32),
256     compare!(F64Eq, f64),
257     compare!(F32Ne, f32),
258     compare!(F64Ne, f64),
259     compare!(F32Lt, f32),
260     compare!(F64Lt, f64),
261     compare!(F32Gt, f32),
262     compare!(F64Gt, f64),
263     compare!(F32Le, f32),
264     compare!(F64Le, f64),
265     compare!(F32Ge, f32),
266     compare!(F64Ge, f64),
267     // Integer conversions ("to integer").
268     unary!(I32Extend8S, i32, i32, |c| c.config.sign_extension_enabled),
269     unary!(I32Extend16S, i32, i32, |c| c.config.sign_extension_enabled),
270     unary!(I64Extend8S, i64, i64, |c| c.config.sign_extension_enabled),
271     unary!(I64Extend16S, i64, i64, |c| c.config.sign_extension_enabled),
272     convert!(I64Extend32S, i64 -> i64, |c| c.config.sign_extension_enabled),
273     convert!(I32WrapI64, i64 -> i32),
274     convert!(I64ExtendI32S, i32 -> i64),
275     convert!(I64ExtendI32U, i32 -> i64),
276     convert!(I32TruncF32S, f32 -> i32),
277     convert!(I32TruncF32U, f32 -> i32),
278     convert!(I32TruncF64S, f64 -> i32),
279     convert!(I32TruncF64U, f64 -> i32),
280     convert!(I64TruncF32S, f32 -> i64),
281     convert!(I64TruncF32U, f32 -> i64),
282     convert!(I64TruncF64S, f64 -> i64),
283     convert!(I64TruncF64U, f64 -> i64),
284     convert!(I32TruncSatF32S, f32 -> i32, |c| c.config.saturating_float_to_int_enabled),
285     convert!(I32TruncSatF32U, f32 -> i32, |c| c.config.saturating_float_to_int_enabled),
286     convert!(I32TruncSatF64S, f64 -> i32, |c| c.config.saturating_float_to_int_enabled),
287     convert!(I32TruncSatF64U, f64 -> i32, |c| c.config.saturating_float_to_int_enabled),
288     convert!(I64TruncSatF32S, f32 -> i64, |c| c.config.saturating_float_to_int_enabled),
289     convert!(I64TruncSatF32U, f32 -> i64, |c| c.config.saturating_float_to_int_enabled),
290     convert!(I64TruncSatF64S, f64 -> i64, |c| c.config.saturating_float_to_int_enabled),
291     convert!(I64TruncSatF64U, f64 -> i64, |c| c.config.saturating_float_to_int_enabled),
292     convert!(I32ReinterpretF32, f32 -> i32),
293     convert!(I64ReinterpretF64, f64 -> i64),
294     // Floating-point conversions ("to float").
295     convert!(F32DemoteF64, f64 -> f32),
296     convert!(F64PromoteF32, f32 -> f64),
297     convert!(F32ConvertI32S, i32 -> f32),
298     convert!(F32ConvertI32U, i32 -> f32),
299     convert!(F32ConvertI64S, i64 -> f32),
300     convert!(F32ConvertI64U, i64 -> f32),
301     convert!(F64ConvertI32S, i32 -> f64),
302     convert!(F64ConvertI32U, i32 -> f64),
303     convert!(F64ConvertI64S, i64 -> f64),
304     convert!(F64ConvertI64U, i64 -> f64),
305     convert!(F32ReinterpretI32, i32 -> f32),
306     convert!(F64ReinterpretI64, i64 -> f64),
307 ];
308 
309 #[cfg(test)]
310 mod test {
311     use super::*;
312 
313     #[test]
314     fn sanity() {
315         let sut = SingleInstModule {
316             instruction: Instruction::I32Add,
317             parameters: &[ValType::I32, ValType::I32],
318             results: &[ValType::I32],
319             feature: |_| true,
320         };
321         let wasm = sut.to_bytes();
322         let wat = wasmprinter::print_bytes(wasm).unwrap();
323         assert_eq!(
324             wat,
325             r#"(module
326   (type (;0;) (func (param i32 i32) (result i32)))
327   (func (;0;) (type 0) (param i32 i32) (result i32)
328     local.get 0
329     local.get 1
330     i32.add
331   )
332   (export "test" (func 0))
333 )"#
334         )
335     }
336 
337     #[test]
338     fn instructions_encode_to_valid_modules() {
339         for inst in INSTRUCTIONS {
340             assert!(wat::parse_bytes(&inst.to_bytes()).is_ok());
341         }
342     }
343 }
344