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