/src/wasm-tools/crates/wasm-smith/src/config.rs
Line | Count | Source |
1 | | //! Configuring the shape of generated Wasm modules. |
2 | | |
3 | | use crate::InstructionKinds; |
4 | | use anyhow::bail; |
5 | | use arbitrary::{Arbitrary, Result, Unstructured}; |
6 | | |
7 | | macro_rules! define_config { |
8 | | ( |
9 | | $(#[$attr:meta])* |
10 | | pub struct Config { |
11 | | $( |
12 | | $(#[$field_attr:meta])* |
13 | | pub $field:ident : $field_ty:ty = $default:expr, |
14 | | )* |
15 | | } |
16 | | ) => { |
17 | | $(#[$attr])* |
18 | | pub struct Config { |
19 | | /// The imports that may be used when generating the module. |
20 | | /// |
21 | | /// Defaults to `None` which means that any arbitrary import can be |
22 | | /// generated. |
23 | | /// |
24 | | /// To only allow specific imports, set this field to a WebAssembly |
25 | | /// module which describes the imports allowed. |
26 | | /// |
27 | | /// Note that [`Self::min_imports`] is ignored when |
28 | | /// `available_imports` are enabled. |
29 | | /// |
30 | | /// The provided value must be a valid binary encoding of a |
31 | | /// WebAssembly module. `wasm-smith` will panic if the module cannot |
32 | | /// be parsed. |
33 | | /// |
34 | | /// # Example |
35 | | /// |
36 | | /// An implementation of this method could use the `wat` crate to |
37 | | /// provide a human-readable and maintainable description: |
38 | | /// |
39 | | /// ```rust |
40 | | /// Some(wat::parse_str(r#" |
41 | | /// (module |
42 | | /// (import "env" "ping" (func (param i32))) |
43 | | /// (import "env" "pong" (func (result i32))) |
44 | | /// (import "env" "memory" (memory 1)) |
45 | | /// (import "env" "table" (table 1)) |
46 | | /// (import "env" "tag" (tag (param i32))) |
47 | | /// ) |
48 | | /// "#)) |
49 | | /// # ; |
50 | | /// ``` |
51 | | pub available_imports: Option<Vec<u8>>, |
52 | | |
53 | | /// If provided, the generated module will have exports with exactly |
54 | | /// the same names and types as those in the provided WebAssembly |
55 | | /// module. The implementation (e.g. function bodies, global |
56 | | /// initializers) of each export in the generated module will be |
57 | | /// random and unrelated to the implementation in the provided |
58 | | /// module. |
59 | | /// |
60 | | /// |
61 | | /// Defaults to `None` which means arbitrary exports will be |
62 | | /// generated. |
63 | | /// |
64 | | /// To specify which exports the generated modules should have, set |
65 | | /// this field to a WebAssembly module which describes the desired |
66 | | /// exports. To generate modules with varying exports that meet some |
67 | | /// constraints, consider randomly generating the value for this |
68 | | /// field. |
69 | | /// |
70 | | /// The provided value must be a valid binary encoding of a |
71 | | /// WebAssembly module. `wasm-smith` will panic if the module cannot |
72 | | /// be parsed. |
73 | | /// |
74 | | /// # Module Limits |
75 | | /// |
76 | | /// All types, functions, globals, memories, tables, tags, and exports |
77 | | /// that are needed to provide the required exports will be generated, |
78 | | /// even if it causes the resulting module to exceed the limits defined |
79 | | /// in [`Self::max_type_size`], [`Self::max_types`], |
80 | | /// [`Self::max_funcs`], [`Self::max_globals`], |
81 | | /// [`Self::max_memories`], [`Self::max_tables`], |
82 | | /// [`Self::max_tags`], or [`Self::max_exports`]. |
83 | | /// |
84 | | /// # Example |
85 | | /// |
86 | | /// As for [`Self::available_imports`], the `wat` crate can be used |
87 | | /// to provide an human-readable description of the desired exports: |
88 | | /// |
89 | | /// ```rust |
90 | | /// Some(wat::parse_str(r#" |
91 | | /// (module |
92 | | /// (func (export "foo") (param i32) (result i64) unreachable) |
93 | | /// (global (export "bar") f32 f32.const 0) |
94 | | /// (memory (export "baz") 1 10) |
95 | | /// (table (export "qux") 5 10 (ref null extern)) |
96 | | /// (tag (export "quux") (param f32)) |
97 | | /// ) |
98 | | /// "#)); |
99 | | /// ``` |
100 | | pub exports: Option<Vec<u8>>, |
101 | | |
102 | | /// If provided, the generated module will have imports and exports |
103 | | /// with exactly the same names and types as those in the provided |
104 | | /// WebAssembly module. |
105 | | /// |
106 | | /// Defaults to `None` which means arbitrary imports and exports will be |
107 | | /// generated. |
108 | | /// |
109 | | /// Note that [`Self::available_imports`] and [`Self::exports`] are |
110 | | /// ignored when `module_shape` is enabled. |
111 | | /// |
112 | | /// The provided value must be a valid binary encoding of a |
113 | | /// WebAssembly module. `wasm-smith` will panic if the module cannot |
114 | | /// be parsed. |
115 | | /// |
116 | | /// # Module Limits |
117 | | /// |
118 | | /// All types, functions, globals, memories, tables, tags, imports, and exports |
119 | | /// that are needed to provide the required imports and exports will be generated, |
120 | | /// even if it causes the resulting module to exceed the limits defined in |
121 | | /// [`Self::max_type_size`], [`Self::max_types`], [`Self::max_funcs`], |
122 | | /// [`Self::max_globals`], [`Self::max_memories`], [`Self::max_tables`], |
123 | | /// [`Self::max_tags`], [`Self::max_imports`], or [`Self::max_exports`]. |
124 | | /// |
125 | | /// # Example |
126 | | /// |
127 | | /// As for [`Self::available_imports`] and [`Self::exports`], the |
128 | | /// `wat` crate can be used to provide a human-readable description of the |
129 | | /// module shape: |
130 | | /// |
131 | | /// ```rust |
132 | | /// Some(wat::parse_str(r#" |
133 | | /// (module |
134 | | /// (import "env" "ping" (func (param i32))) |
135 | | /// (import "env" "memory" (memory 1)) |
136 | | /// (func (export "foo") (param anyref) (result structref) unreachable) |
137 | | /// (global (export "bar") arrayref (ref.null array)) |
138 | | /// ) |
139 | | /// "#)); |
140 | | /// ``` |
141 | | pub module_shape: Option<Vec<u8>>, |
142 | | |
143 | | $( |
144 | | $(#[$field_attr])* |
145 | | pub $field: $field_ty, |
146 | | )* |
147 | | } |
148 | | |
149 | | impl Default for Config { |
150 | 0 | fn default() -> Config { |
151 | 0 | Config { |
152 | 0 | available_imports: None, |
153 | 0 | exports: None, |
154 | 0 | module_shape: None, |
155 | 0 |
|
156 | 0 | $( |
157 | 0 | $field: $default, |
158 | 0 | )* |
159 | 0 | } |
160 | 0 | } |
161 | | } |
162 | | |
163 | | #[doc(hidden)] |
164 | | #[derive(Clone, Debug, Default)] |
165 | | #[cfg_attr(feature = "clap", derive(clap::Parser))] |
166 | | #[cfg_attr(feature = "serde", derive(serde_derive::Deserialize, serde_derive::Serialize))] |
167 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case", deny_unknown_fields))] |
168 | | pub struct InternalOptionalConfig { |
169 | | /// The imports that may be used when generating the module. |
170 | | /// |
171 | | /// When unspecified, any arbitrary import can be generated. |
172 | | /// |
173 | | /// To only allow specific imports, provide a file path of a |
174 | | /// WebAssembly module which describes the imports allowed. |
175 | | /// |
176 | | /// Note that [`Self::min_imports`] is ignored when |
177 | | /// `available_imports` are enabled. |
178 | | /// |
179 | | /// The provided value must be a valid binary encoding of a |
180 | | /// WebAssembly module. `wasm-smith` will panic if the module cannot |
181 | | /// be parsed. |
182 | | #[cfg_attr(feature = "clap", clap(long))] |
183 | | available_imports: Option<std::path::PathBuf>, |
184 | | |
185 | | /// If provided, the generated module will have exports with exactly |
186 | | /// the same names and types as those in the provided WebAssembly |
187 | | /// module. The implementation (e.g. function bodies, global |
188 | | /// initializers) of each export in the generated module will be |
189 | | /// random and unrelated to the implementation in the provided |
190 | | /// module. |
191 | | /// |
192 | | /// Defaults to `None` which means arbitrary exports will be |
193 | | /// generated. |
194 | | /// |
195 | | /// To specify which exports the generated modules should have, set |
196 | | /// this field to a WebAssembly module which describes the desired |
197 | | /// exports. To generate modules with varying exports that meet some |
198 | | /// constraints, consider randomly generating the value for this |
199 | | /// field. |
200 | | /// |
201 | | /// The provided value must be a valid binary encoding of a |
202 | | /// WebAssembly module. `wasm-smith` will panic if the module cannot |
203 | | /// be parsed. |
204 | | /// |
205 | | /// # Module Limits |
206 | | /// |
207 | | /// All types, functions, globals, memories, tables, tags, and exports |
208 | | /// that are needed to provide the required exports will be generated, |
209 | | /// even if it causes the resulting module to exceed the limits defined |
210 | | /// in [`Self::max_type_size`], [`Self::max_types`], |
211 | | /// [`Self::max_funcs`], [`Self::max_globals`], |
212 | | /// [`Self::max_memories`], [`Self::max_tables`], |
213 | | /// [`Self::max_tags`], or [`Self::max_exports`]. |
214 | | /// |
215 | | #[cfg_attr(feature = "clap", clap(long))] |
216 | | exports: Option<std::path::PathBuf>, |
217 | | |
218 | | /// If provided, the generated module will have imports and exports |
219 | | /// with exactly the same names and types as those in the provided |
220 | | /// WebAssembly module. |
221 | | /// |
222 | | /// Defaults to `None` which means arbitrary imports and exports will be |
223 | | /// generated. |
224 | | /// |
225 | | /// Note that [`Self::available_imports`] and [`Self::exports`] are |
226 | | /// ignored when `module_shape` is enabled. |
227 | | /// |
228 | | /// The provided value must be a valid binary encoding of a |
229 | | /// WebAssembly module. `wasm-smith` will panic if the module cannot |
230 | | /// be parsed. |
231 | | /// |
232 | | /// # Module Limits |
233 | | /// |
234 | | /// All types, functions, globals, memories, tables, tags, imports, and exports |
235 | | /// that are needed to provide the required imports and exports will be generated, |
236 | | /// even if it causes the resulting module to exceed the limits defined in |
237 | | /// [`Self::max_type_size`], [`Self::max_types`], [`Self::max_funcs`], |
238 | | /// [`Self::max_globals`], [`Self::max_memories`], [`Self::max_tables`], |
239 | | /// [`Self::max_tags`], [`Self::max_imports`], or [`Self::max_exports`]. |
240 | | #[cfg_attr(feature = "clap", clap(long))] |
241 | | module_shape: Option<std::path::PathBuf>, |
242 | | |
243 | | $( |
244 | | $(#[$field_attr])* |
245 | | #[cfg_attr(feature = "clap", clap(long))] |
246 | | pub $field: Option<$field_ty>, |
247 | | )* |
248 | | } |
249 | | |
250 | | impl InternalOptionalConfig { |
251 | 0 | pub fn or(self, other: Self) -> Self { |
252 | 0 | Self { |
253 | 0 | available_imports: self.available_imports.or(other.available_imports), |
254 | 0 | exports: self.exports.or(other.exports), |
255 | 0 | module_shape: self.module_shape.or(other.module_shape), |
256 | 0 |
|
257 | 0 | $( |
258 | 0 | $field: self.$field.or(other.$field), |
259 | 0 | )* |
260 | 0 | } |
261 | 0 | } |
262 | | } |
263 | | |
264 | | #[cfg(feature = "serde")] |
265 | | impl TryFrom<InternalOptionalConfig> for Config { |
266 | | type Error = anyhow::Error; |
267 | | fn try_from(config: InternalOptionalConfig) -> anyhow::Result<Config> { |
268 | | let default = Config::default(); |
269 | | Ok(Config { |
270 | | available_imports: if let Some(file) = config |
271 | | .available_imports |
272 | | .as_ref() { |
273 | | Some(wat::parse_file(file)?) |
274 | | } else { |
275 | | None |
276 | | }, |
277 | | exports: if let Some(file) = config |
278 | | .exports |
279 | | .as_ref() { |
280 | | Some(wat::parse_file(file)?) |
281 | | } else { |
282 | | None |
283 | | }, |
284 | | module_shape: if let Some(file) = config |
285 | | .module_shape |
286 | | .as_ref() { |
287 | | Some(wat::parse_file(file)?) |
288 | | } else { |
289 | | None |
290 | | }, |
291 | | |
292 | | $( |
293 | | $field: config.$field.unwrap_or(default.$field), |
294 | | )* |
295 | | }) |
296 | | } |
297 | | } |
298 | | |
299 | | impl TryFrom<&Config> for InternalOptionalConfig { |
300 | | type Error = anyhow::Error; |
301 | 0 | fn try_from(config: &Config) -> anyhow::Result<InternalOptionalConfig> { |
302 | 0 | if config.available_imports.is_some() { |
303 | 0 | bail!("cannot serialize configuration with `available_imports`"); |
304 | 0 | } |
305 | 0 | if config.exports.is_some() { |
306 | 0 | bail!("cannot serialize configuration with `exports`"); |
307 | 0 | } |
308 | 0 | if config.module_shape.is_some() { |
309 | 0 | bail!("cannot serialize configuration with `module_shape`"); |
310 | 0 | } |
311 | 0 | Ok(InternalOptionalConfig { |
312 | 0 | available_imports: None, |
313 | 0 | exports: None, |
314 | 0 | module_shape: None, |
315 | 0 | $( $field: Some(config.$field.clone()), )* |
316 | 0 | }) |
317 | 0 | } |
318 | | } |
319 | | } |
320 | | } |
321 | | |
322 | | define_config! { |
323 | | /// Configuration for a generated module. |
324 | | /// |
325 | | /// Don't care to configure your generated modules? Just use |
326 | | /// [`Module::arbitrary`][crate::Module], which internally uses the default |
327 | | /// configuration. |
328 | | /// |
329 | | /// Want control over the shape of the module that gets generated? Create a |
330 | | /// `Config` and then pass it to [`Module::new`][crate::Module::new]. |
331 | | /// |
332 | | /// # Swarm Testing |
333 | | /// |
334 | | /// You can use the `Arbitrary for Config` implementation for [swarm |
335 | | /// testing]. This will dynamically -- but still deterministically -- choose |
336 | | /// configuration options for you. |
337 | | /// |
338 | | /// [swarm testing]: https://www.cs.utah.edu/~regehr/papers/swarm12.pdf |
339 | | /// |
340 | | /// Note that we pick only *maximums*, not minimums, here because it is more |
341 | | /// complex to describe the domain of valid configs when minima are involved |
342 | | /// (`min <= max` for each variable) and minima are mostly used to ensure |
343 | | /// certain elements are present, but do not widen the range of generated |
344 | | /// Wasm modules. |
345 | | #[derive(Clone, Debug)] |
346 | | pub struct Config { |
347 | | /// Determines whether a `start` export may be included. Defaults to `true`. |
348 | | pub allow_start_export: bool = true, |
349 | | |
350 | | /// The kinds of instructions allowed in the generated wasm |
351 | | /// programs. Defaults to all. |
352 | | /// |
353 | | /// The categories of instructions match the categories used by the |
354 | | /// [WebAssembly |
355 | | /// specification](https://webassembly.github.io/spec/core/syntax/instructions.html); |
356 | | /// e.g., numeric, vector, control, memory, etc. |
357 | | /// |
358 | | /// Additionally, we include finer-grained categories which exclude floating point |
359 | | /// instructions, e.g. [`InstructionKind::NumericInt`] is a subset of |
360 | | /// [`InstructionKind::Numeric`] consisting of all numeric instructions which |
361 | | /// don't involve floats. |
362 | | /// |
363 | | /// Note that modifying this setting is separate from the proposal |
364 | | /// flags; that is, if `simd_enabled() == true` but |
365 | | /// `allowed_instruction()` does not include vector instructions, the |
366 | | /// generated programs will not include these instructions but could |
367 | | /// contain vector types. |
368 | | /// |
369 | | /// [`InstructionKind::Numeric`]: crate::InstructionKind::Numeric |
370 | | /// [`InstructionKind::NumericInt`]: crate::InstructionKind::NumericInt |
371 | | pub allowed_instructions: InstructionKinds = InstructionKinds::all(), |
372 | | |
373 | | /// Determines whether we generate floating point instructions and types. |
374 | | /// |
375 | | /// Defaults to `true`. |
376 | | pub allow_floats: bool = true, |
377 | | |
378 | | /// Determines whether the bulk memory proposal is enabled for |
379 | | /// generating instructions. |
380 | | /// |
381 | | /// Defaults to `true`. |
382 | | pub bulk_memory_enabled: bool = true, |
383 | | |
384 | | /// Returns whether NaN values are canonicalized after all f32/f64 |
385 | | /// operation. Defaults to false. |
386 | | /// |
387 | | /// This can be useful when a generated wasm module is executed in |
388 | | /// multiple runtimes which may produce different NaN values. This |
389 | | /// ensures that the generated module will always use the same NaN |
390 | | /// representation for all instructions which have visible side effects, |
391 | | /// for example writing floats to memory or float-to-int bitcast |
392 | | /// instructions. |
393 | | pub canonicalize_nans: bool = false, |
394 | | |
395 | | /// Returns whether we should avoid generating code that will possibly |
396 | | /// trap. |
397 | | /// |
398 | | /// For some trapping instructions, this will emit extra instructions to |
399 | | /// ensure they don't trap, while some instructions will simply be |
400 | | /// excluded. In cases where we would run into a trap, we instead |
401 | | /// choose some arbitrary non-trapping behavior. For example, if we |
402 | | /// detect that a Load instruction would attempt to access out-of-bounds |
403 | | /// memory, we instead pretend the load succeeded and push 0 onto the |
404 | | /// stack. |
405 | | /// |
406 | | /// One type of trap that we can't currently avoid is |
407 | | /// StackOverflow. Even when `disallow_traps` is set to true, wasm-smith |
408 | | /// will eventually generate a program that infinitely recurses, causing |
409 | | /// the call stack to be exhausted. |
410 | | /// |
411 | | /// Defaults to `false`. |
412 | | pub disallow_traps: bool = false, |
413 | | |
414 | | /// Determines whether the exception-handling proposal is enabled for |
415 | | /// generating instructions. |
416 | | /// |
417 | | /// Defaults to `true`. |
418 | | pub exceptions_enabled: bool = true, |
419 | | |
420 | | /// Export all WebAssembly objects in the module. Defaults to false. |
421 | | /// |
422 | | /// This overrides [`Config::min_exports`] and [`Config::max_exports`]. |
423 | | pub export_everything: bool = false, |
424 | | |
425 | | /// Determines whether the GC proposal is enabled when generating a Wasm |
426 | | /// module. |
427 | | /// |
428 | | /// Defaults to `true`. |
429 | | pub gc_enabled: bool = true, |
430 | | |
431 | | /// Determines whether the custom descriptors proposal is enabled when |
432 | | /// generating a Wasm module. |
433 | | /// |
434 | | /// Defaults to `true`. |
435 | | pub custom_descriptors_enabled: bool = false, |
436 | | |
437 | | /// Determines whether the custom-page-sizes proposal is enabled when |
438 | | /// generating a Wasm module. |
439 | | /// |
440 | | /// Defaults to `false`. |
441 | | pub custom_page_sizes_enabled: bool = false, |
442 | | |
443 | | /// Returns whether we should generate custom sections or not. Defaults |
444 | | /// to false. |
445 | | pub generate_custom_sections: bool = false, |
446 | | |
447 | | /// Returns the maximal size of the `alias` section. Defaults to 1000. |
448 | | pub max_aliases: usize = 1000, |
449 | | |
450 | | /// The maximum number of components to use. Defaults to 10. |
451 | | /// |
452 | | /// This includes imported components. |
453 | | /// |
454 | | /// Note that this is only relevant for components. |
455 | | pub max_components: usize = 10, |
456 | | |
457 | | /// The maximum number of data segments to generate. Defaults to 100. |
458 | | pub max_data_segments: usize = 100, |
459 | | |
460 | | /// The maximum number of element segments to generate. Defaults to 100. |
461 | | pub max_element_segments: usize = 100, |
462 | | |
463 | | /// The maximum number of elements within a segment to |
464 | | /// generate. Defaults to 100. |
465 | | pub max_elements: usize = 100, |
466 | | |
467 | | /// The maximum number of exports to generate. Defaults to 100. |
468 | | pub max_exports: usize = 100, |
469 | | |
470 | | /// The maximum number of functions to generate. Defaults to 100. This |
471 | | /// includes imported functions. |
472 | | pub max_funcs: usize = 100, |
473 | | |
474 | | /// The maximum number of globals to generate. Defaults to 100. This |
475 | | /// includes imported globals. |
476 | | pub max_globals: usize = 100, |
477 | | |
478 | | /// The maximum number of imports to generate. Defaults to 100. |
479 | | pub max_imports: usize = 100, |
480 | | |
481 | | /// The maximum number of instances to use. Defaults to 10. |
482 | | /// |
483 | | /// This includes imported instances. |
484 | | /// |
485 | | /// Note that this is only relevant for components. |
486 | | pub max_instances: usize = 10, |
487 | | |
488 | | /// The maximum number of instructions to generate in a function |
489 | | /// body. Defaults to 100. |
490 | | /// |
491 | | /// Note that some additional `end`s, `else`s, and `unreachable`s may be |
492 | | /// appended to the function body to finish block scopes. |
493 | | pub max_instructions: usize = 100, |
494 | | |
495 | | /// The maximum number of memories to use. Defaults to 1. |
496 | | /// |
497 | | /// This includes imported memories. |
498 | | /// |
499 | | /// Note that more than one memory is in the realm of the multi-memory |
500 | | /// wasm proposal. |
501 | | pub max_memories: usize = 1, |
502 | | |
503 | | /// The maximum, in bytes, of any 32-bit memory's initial or maximum |
504 | | /// size. |
505 | | /// |
506 | | /// May not be larger than `2**32`. |
507 | | /// |
508 | | /// Defaults to `2**32`. |
509 | | pub max_memory32_bytes: u64 = u32::MAX as u64 + 1, |
510 | | |
511 | | /// The maximum, in bytes, of any 64-bit memory's initial or maximum |
512 | | /// size. |
513 | | /// |
514 | | /// May not be larger than `2**64`. |
515 | | /// |
516 | | /// Defaults to `2**64`. |
517 | | pub max_memory64_bytes: u128 = u64::MAX as u128 + 1, |
518 | | |
519 | | /// The maximum number of modules to use. Defaults to 10. |
520 | | /// |
521 | | /// This includes imported modules. |
522 | | /// |
523 | | /// Note that this is only relevant for components. |
524 | | pub max_modules: usize = 10, |
525 | | |
526 | | /// Returns the maximal nesting depth of modules with the component |
527 | | /// model proposal. Defaults to 10. |
528 | | pub max_nesting_depth: usize = 10, |
529 | | |
530 | | /// The maximum, elements, of any table's initial or maximum |
531 | | /// size. Defaults to 1 million. |
532 | | pub max_table_elements: u64 = 1_000_000, |
533 | | |
534 | | /// The maximum number of tables to use. Defaults to 1. |
535 | | /// |
536 | | /// This includes imported tables. |
537 | | /// |
538 | | /// Note that more than one table is in the realm of the reference types |
539 | | /// proposal. |
540 | | pub max_tables: usize = 1, |
541 | | |
542 | | /// The maximum number of tags to generate. Defaults to 100. |
543 | | pub max_tags: usize = 100, |
544 | | |
545 | | /// Returns the maximal effective size of any type generated by |
546 | | /// wasm-smith. |
547 | | /// |
548 | | /// Note that this number is roughly in units of "how many types would |
549 | | /// be needed to represent the recursive type". A function with 8 |
550 | | /// parameters and 2 results would take 11 types (one for the type, 10 |
551 | | /// for params/results). A module type with 2 imports and 3 exports |
552 | | /// would take 6 (module + imports + exports) plus the size of each |
553 | | /// import/export type. This is a somewhat rough measurement that is not |
554 | | /// intended to be very precise. |
555 | | /// |
556 | | /// Defaults to 1000. |
557 | | pub max_type_size: u32 = 1000, |
558 | | |
559 | | /// The maximum number of types to generate. Defaults to 100. |
560 | | pub max_types: usize = 100, |
561 | | |
562 | | /// The maximum number of values to use. Defaults to 10. |
563 | | /// |
564 | | /// This includes imported values. |
565 | | /// |
566 | | /// Note that this is irrelevant unless value model support is enabled. |
567 | | pub max_values: usize = 10, |
568 | | |
569 | | /// Returns whether 64-bit memories are allowed. Defaults to true. |
570 | | /// |
571 | | /// Note that this is the gate for the memory64 proposal to WebAssembly. |
572 | | pub memory64_enabled: bool = true, |
573 | | |
574 | | /// Whether every Wasm memory must have a maximum size |
575 | | /// specified. Defaults to `false`. |
576 | | pub memory_max_size_required: bool = false, |
577 | | |
578 | | /// Control the probability of generating memory offsets that are in |
579 | | /// bounds vs. potentially out of bounds. |
580 | | /// |
581 | | /// See the `MemoryOffsetChoices` struct for details. |
582 | | pub memory_offset_choices: MemoryOffsetChoices = MemoryOffsetChoices::default(), |
583 | | |
584 | | /// The minimum number of data segments to generate. Defaults to 0. |
585 | | pub min_data_segments: usize = 0, |
586 | | |
587 | | /// The minimum number of element segments to generate. Defaults to 0. |
588 | | pub min_element_segments: usize = 0, |
589 | | |
590 | | /// The minimum number of elements within a segment to |
591 | | /// generate. Defaults to 0. |
592 | | pub min_elements: usize = 0, |
593 | | |
594 | | /// The minimum number of exports to generate. Defaults to 0. |
595 | | pub min_exports: usize = 0, |
596 | | |
597 | | /// The minimum number of functions to generate. Defaults to 0. |
598 | | /// |
599 | | /// This includes imported functions. |
600 | | pub min_funcs: usize = 0, |
601 | | |
602 | | /// The minimum number of globals to generate. Defaults to 0. |
603 | | /// |
604 | | /// This includes imported globals. |
605 | | pub min_globals: usize = 0, |
606 | | |
607 | | /// The minimum number of imports to generate. Defaults to 0. |
608 | | /// |
609 | | /// Note that if the sum of the maximum function[^1], table, global and |
610 | | /// memory counts is less than the minimum number of imports, then it |
611 | | /// will not be possible to satisfy all constraints (because imports |
612 | | /// count against the limits for those element kinds). In that case, we |
613 | | /// strictly follow the max-constraints, and can fail to satisfy this |
614 | | /// minimum number. |
615 | | /// |
616 | | /// [^1]: the maximum number of functions is also limited by the number |
617 | | /// of function types arbitrarily chosen; strictly speaking, then, the |
618 | | /// maximum number of imports that can be created due to max-constraints |
619 | | /// is `sum(min(num_func_types, max_funcs), max_tables, max_globals, |
620 | | /// max_memories)`. |
621 | | pub min_imports: usize = 0, |
622 | | |
623 | | /// The minimum number of memories to use. Defaults to 0. |
624 | | /// |
625 | | /// This includes imported memories. |
626 | | pub min_memories: u32 = 0, |
627 | | |
628 | | /// The minimum number of tables to use. Defaults to 0. |
629 | | /// |
630 | | /// This includes imported tables. |
631 | | pub min_tables: u32 = 0, |
632 | | |
633 | | /// The minimum number of tags to generate. Defaults to 0. |
634 | | pub min_tags: usize = 0, |
635 | | |
636 | | /// The minimum number of types to generate. Defaults to 0. |
637 | | pub min_types: usize = 0, |
638 | | |
639 | | /// The minimum size, in bytes, of all leb-encoded integers. Defaults to |
640 | | /// 1. |
641 | | /// |
642 | | /// This is useful for ensuring that all leb-encoded integers are |
643 | | /// decoded as such rather than as simply one byte. This will forcibly |
644 | | /// extend leb integers with an over-long encoding in some locations if |
645 | | /// the size would otherwise be smaller than number returned here. |
646 | | pub min_uleb_size: u8 = 1, |
647 | | |
648 | | /// Determines whether the multi-value results are enabled. |
649 | | /// |
650 | | /// Defaults to `true`. |
651 | | pub multi_value_enabled: bool = true, |
652 | | |
653 | | /// Determines whether the reference types proposal is enabled for |
654 | | /// generating instructions. |
655 | | /// |
656 | | /// Defaults to `true`. |
657 | | pub reference_types_enabled: bool = true, |
658 | | |
659 | | /// Determines whether the Relaxed SIMD proposal is enabled for |
660 | | /// generating instructions. |
661 | | /// |
662 | | /// Defaults to `true`. |
663 | | pub relaxed_simd_enabled: bool = true, |
664 | | |
665 | | /// Determines whether the non-trapping float-to-int conversions |
666 | | /// proposal is enabled. |
667 | | /// |
668 | | /// Defaults to `true`. |
669 | | pub saturating_float_to_int_enabled: bool = true, |
670 | | |
671 | | /// Determines whether the sign-extension-ops proposal is enabled. |
672 | | /// |
673 | | /// Defaults to `true`. |
674 | | pub sign_extension_ops_enabled: bool = true, |
675 | | |
676 | | /// Determines whether the shared-everything-threads proposal is |
677 | | /// enabled. |
678 | | /// |
679 | | /// The [shared-everything-threads] proposal, among other things, |
680 | | /// extends `shared` attributes to all WebAssembly objects; it builds on |
681 | | /// the [threads] proposal. |
682 | | /// |
683 | | /// [shared-everything-threads]: https://github.com/WebAssembly/shared-everything-threads |
684 | | /// [threads]: https://github.com/WebAssembly/threads |
685 | | /// |
686 | | /// Defaults to `false`. |
687 | | pub shared_everything_threads_enabled: bool = false, |
688 | | |
689 | | /// Determines whether the SIMD proposal is enabled for generating |
690 | | /// instructions. |
691 | | /// |
692 | | /// Defaults to `true`. |
693 | | pub simd_enabled: bool = true, |
694 | | |
695 | | /// Determines whether the tail calls proposal is enabled for generating |
696 | | /// instructions. |
697 | | /// |
698 | | /// Defaults to `true`. |
699 | | pub tail_call_enabled: bool = true, |
700 | | |
701 | | /// Whether every Wasm table must have a maximum size |
702 | | /// specified. Defaults to `false`. |
703 | | pub table_max_size_required: bool = false, |
704 | | |
705 | | /// Determines whether the threads proposal is enabled. |
706 | | /// |
707 | | /// The [threads proposal] involves shared linear memory, new atomic |
708 | | /// instructions, and new `wait` and `notify` instructions. |
709 | | /// |
710 | | /// [threads proposal]: https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md |
711 | | /// |
712 | | /// Defaults to `true`. |
713 | | pub threads_enabled: bool = true, |
714 | | |
715 | | /// Indicates whether wasm-smith is allowed to generate invalid function |
716 | | /// bodies. |
717 | | /// |
718 | | /// When enabled this option will enable taking raw bytes from the input |
719 | | /// byte stream and using them as a wasm function body. This means that |
720 | | /// the output module is not guaranteed to be valid but can help tickle |
721 | | /// various parts of validation/compilation in some circumstances as |
722 | | /// well. |
723 | | /// |
724 | | /// Defaults to `false`. |
725 | | pub allow_invalid_funcs: bool = false, |
726 | | |
727 | | /// Determines whether the [wide-arithmetic proposal] is enabled. |
728 | | /// |
729 | | /// [wide-arithmetic proposal]: https://github.com/WebAssembly/wide-arithmetic |
730 | | /// |
731 | | /// Defaults to `false`. |
732 | | pub wide_arithmetic_enabled: bool = false, |
733 | | |
734 | | /// Determines whether the [extended-const proposal] is enabled. |
735 | | /// |
736 | | /// [extended-const proposal]: https://github.com/WebAssembly/extended-const |
737 | | /// |
738 | | /// Defaults to `true`. |
739 | | pub extended_const_enabled: bool = true, |
740 | | } |
741 | | } |
742 | | |
743 | | /// This is a tuple `(a, b, c)` where |
744 | | /// |
745 | | /// * `a / (a+b+c)` is the probability of generating a memory offset within |
746 | | /// `0..memory.min_size`, i.e. an offset that is definitely in bounds of a |
747 | | /// non-empty memory. (Note that if a memory is zero-sized, however, no offset |
748 | | /// will ever be in bounds.) |
749 | | /// |
750 | | /// * `b / (a+b+c)` is the probability of generating a memory offset within |
751 | | /// `memory.min_size..memory.max_size`, i.e. an offset that is possibly in |
752 | | /// bounds if the memory has been grown. |
753 | | /// |
754 | | /// * `c / (a+b+c)` is the probability of generating a memory offset within the |
755 | | /// range `memory.max_size..`, i.e. an offset that is definitely out of |
756 | | /// bounds. |
757 | | /// |
758 | | /// At least one of `a`, `b`, and `c` must be non-zero. |
759 | | /// |
760 | | /// If you want to always generate memory offsets that are definitely in bounds |
761 | | /// of a non-zero-sized memory, for example, you could return `(1, 0, 0)`. |
762 | | /// |
763 | | /// The default is `(90, 9, 1)`. |
764 | | #[derive(Clone, Debug)] |
765 | | #[cfg_attr( |
766 | | feature = "serde", |
767 | | derive(serde_derive::Deserialize, serde_derive::Serialize) |
768 | | )] |
769 | | pub struct MemoryOffsetChoices(pub u32, pub u32, pub u32); |
770 | | |
771 | | impl Default for MemoryOffsetChoices { |
772 | 19.5k | fn default() -> Self { |
773 | 19.5k | MemoryOffsetChoices(90, 9, 1) |
774 | 19.5k | } |
775 | | } |
776 | | |
777 | | impl std::str::FromStr for MemoryOffsetChoices { |
778 | | type Err = String; |
779 | 0 | fn from_str(s: &str) -> Result<Self, Self::Err> { |
780 | | use std::str::FromStr; |
781 | 0 | let mut parts = s.split(","); |
782 | 0 | let a = parts |
783 | 0 | .next() |
784 | 0 | .ok_or_else(|| "need 3 comma separated values".to_string())?; |
785 | 0 | let a = <u32 as FromStr>::from_str(a).map_err(|e| e.to_string())?; |
786 | 0 | let b = parts |
787 | 0 | .next() |
788 | 0 | .ok_or_else(|| "need 3 comma separated values".to_string())?; |
789 | 0 | let b = <u32 as FromStr>::from_str(b).map_err(|e| e.to_string())?; |
790 | 0 | let c = parts |
791 | 0 | .next() |
792 | 0 | .ok_or_else(|| "need 3 comma separated values".to_string())?; |
793 | 0 | let c = <u32 as FromStr>::from_str(c).map_err(|e| e.to_string())?; |
794 | 0 | if parts.next().is_some() { |
795 | 0 | return Err("found more than 3 comma separated values".to_string()); |
796 | 0 | } |
797 | 0 | Ok(MemoryOffsetChoices(a, b, c)) |
798 | 0 | } |
799 | | } |
800 | | |
801 | | impl<'a> Arbitrary<'a> for Config { |
802 | 19.5k | fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> { |
803 | | const MAX_MAXIMUM: usize = 1000; |
804 | | |
805 | 19.5k | let mut config = Config { |
806 | 19.5k | max_types: u.int_in_range(0..=MAX_MAXIMUM)?, |
807 | 19.5k | max_imports: u.int_in_range(0..=MAX_MAXIMUM)?, |
808 | 19.5k | max_tags: u.int_in_range(0..=MAX_MAXIMUM)?, |
809 | 19.5k | max_funcs: u.int_in_range(0..=MAX_MAXIMUM)?, |
810 | 19.5k | max_globals: u.int_in_range(0..=MAX_MAXIMUM)?, |
811 | 19.5k | max_exports: u.int_in_range(0..=MAX_MAXIMUM)?, |
812 | 19.5k | max_element_segments: u.int_in_range(0..=MAX_MAXIMUM)?, |
813 | 19.5k | max_elements: u.int_in_range(0..=MAX_MAXIMUM)?, |
814 | 19.5k | max_data_segments: u.int_in_range(0..=MAX_MAXIMUM)?, |
815 | 19.5k | max_instructions: u.int_in_range(0..=MAX_MAXIMUM)?, |
816 | 19.5k | max_memories: u.int_in_range(0..=100)?, |
817 | 19.5k | max_tables: u.int_in_range(0..=100)?, |
818 | 19.5k | max_memory32_bytes: u.int_in_range(0..=u32::MAX as u64 + 1)?, |
819 | 19.5k | max_memory64_bytes: u.int_in_range(0..=u64::MAX as u128 + 1)?, |
820 | 19.5k | min_uleb_size: u.int_in_range(0..=5)?, |
821 | 19.5k | bulk_memory_enabled: u.arbitrary()?, |
822 | 19.5k | reference_types_enabled: u.arbitrary()?, |
823 | 19.5k | simd_enabled: u.arbitrary()?, |
824 | 19.5k | multi_value_enabled: u.arbitrary()?, |
825 | 19.5k | max_aliases: u.int_in_range(0..=MAX_MAXIMUM)?, |
826 | 19.5k | max_nesting_depth: u.int_in_range(0..=10)?, |
827 | 19.5k | saturating_float_to_int_enabled: u.arbitrary()?, |
828 | 19.5k | sign_extension_ops_enabled: u.arbitrary()?, |
829 | 19.5k | relaxed_simd_enabled: u.arbitrary()?, |
830 | 19.5k | exceptions_enabled: u.arbitrary()?, |
831 | 19.5k | threads_enabled: u.arbitrary()?, |
832 | 19.5k | tail_call_enabled: u.arbitrary()?, |
833 | 19.5k | gc_enabled: u.arbitrary()?, |
834 | 19.5k | memory64_enabled: u.arbitrary()?, |
835 | | allowed_instructions: { |
836 | | use flagset::Flags; |
837 | 19.5k | let mut allowed = Vec::new(); |
838 | 253k | for kind in crate::core::InstructionKind::LIST { |
839 | 234k | if u.arbitrary()? { |
840 | 138k | allowed.push(*kind); |
841 | 138k | } |
842 | | } |
843 | 19.5k | InstructionKinds::new(&allowed) |
844 | | }, |
845 | 19.5k | table_max_size_required: u.arbitrary()?, |
846 | 19.5k | max_table_elements: u.int_in_range(0..=1_000_000)?, |
847 | 19.5k | disallow_traps: u.arbitrary()?, |
848 | 19.5k | allow_floats: u.arbitrary()?, |
849 | 19.5k | extended_const_enabled: u.arbitrary()?, |
850 | | |
851 | | // These fields, unlike the ones above, are less useful to set. |
852 | | // They either make weird inputs or are for features not widely |
853 | | // implemented yet so they're turned off by default. |
854 | | min_types: 0, |
855 | | min_imports: 0, |
856 | | min_tags: 0, |
857 | | min_funcs: 0, |
858 | | min_globals: 0, |
859 | | min_exports: 0, |
860 | | min_element_segments: 0, |
861 | | min_elements: 0, |
862 | | min_data_segments: 0, |
863 | | min_memories: 0, |
864 | | min_tables: 0, |
865 | | memory_max_size_required: false, |
866 | | max_instances: 0, |
867 | | max_modules: 0, |
868 | | max_components: 0, |
869 | | max_values: 0, |
870 | 19.5k | memory_offset_choices: MemoryOffsetChoices::default(), |
871 | | allow_start_export: true, |
872 | | max_type_size: 1000, |
873 | | canonicalize_nans: false, |
874 | 19.5k | available_imports: None, |
875 | 19.5k | exports: None, |
876 | 19.5k | module_shape: None, |
877 | | export_everything: false, |
878 | | generate_custom_sections: false, |
879 | | allow_invalid_funcs: false, |
880 | | |
881 | | // Proposals that are not stage4+ are disabled by default. |
882 | | custom_page_sizes_enabled: false, |
883 | | wide_arithmetic_enabled: false, |
884 | | shared_everything_threads_enabled: false, |
885 | | custom_descriptors_enabled: false, |
886 | | }; |
887 | 19.5k | config.sanitize(); |
888 | 19.5k | Ok(config) |
889 | 19.5k | } |
890 | | } |
891 | | |
892 | | impl Config { |
893 | | /// "Shrink" this `Config` where appropriate to ensure its configuration is |
894 | | /// valid for wasm-smith. |
895 | | /// |
896 | | /// This method will take the arbitrary state that this `Config` is in and |
897 | | /// will possibly mutate dependent options as needed by `wasm-smith`. For |
898 | | /// example if the `reference_types_enabled` field is turned off then |
899 | | /// `wasm-smith`, as of the time of this writing, additionally requires that |
900 | | /// the `gc_enabled` is not turned on. |
901 | | /// |
902 | | /// This method will not enable anything that isn't already enabled or |
903 | | /// increase any limit of an item, but it may turn features off or shrink |
904 | | /// limits from what they're previously specified as. |
905 | 39.0k | pub(crate) fn sanitize(&mut self) { |
906 | | // If reference types are disabled then automatically flag tables as |
907 | | // capped at 1 and disable gc as well. |
908 | 39.0k | if !self.reference_types_enabled { |
909 | 19.2k | self.max_tables = self.max_tables.min(1); |
910 | 19.2k | self.gc_enabled = false; |
911 | 19.2k | self.shared_everything_threads_enabled = false; |
912 | 19.7k | } |
913 | | |
914 | | // shared-everything-threads depends on GC, so if gc is disabled then |
915 | | // also disable shared-everything-threads. |
916 | 39.0k | if !self.gc_enabled { |
917 | 28.3k | self.shared_everything_threads_enabled = false; |
918 | 28.3k | self.custom_descriptors_enabled = false; |
919 | 28.3k | } |
920 | | |
921 | | // If simd is disabled then disable all relaxed simd instructions as |
922 | | // well. |
923 | 39.0k | if !self.simd_enabled { |
924 | 13.8k | self.relaxed_simd_enabled = false; |
925 | 25.1k | } |
926 | | |
927 | | // It is impossible to use the shared-everything-threads proposal |
928 | | // without threads, which it is built on. |
929 | 39.0k | if !self.threads_enabled { |
930 | 21.0k | self.shared_everything_threads_enabled = false; |
931 | 21.0k | } |
932 | | |
933 | | // If module_shape is present then disable available_imports and exports. |
934 | 39.0k | if self.module_shape.is_some() { |
935 | 0 | self.available_imports = None; |
936 | 0 | self.exports = None; |
937 | 39.0k | } |
938 | 39.0k | } |
939 | | |
940 | | /// Returns the set of features that are necessary for validating against |
941 | | /// this `Config`. |
942 | | #[cfg(feature = "wasmparser")] |
943 | 5.40k | pub fn features(&self) -> wasmparser::WasmFeatures { |
944 | | use wasmparser::WasmFeatures; |
945 | | |
946 | | // Currently wasm-smith doesn't have knobs for the MVP (floats) or |
947 | | // `mutable-global`. These are unconditionally enabled. |
948 | 5.40k | let mut features = WasmFeatures::MUTABLE_GLOBAL | WasmFeatures::WASM1; |
949 | | |
950 | | // All other features that can be generated by wasm-smith are gated by |
951 | | // configuration fields. Conditionally set each feature based on the |
952 | | // status of the fields in `self`. |
953 | 5.40k | features.set( |
954 | | WasmFeatures::SATURATING_FLOAT_TO_INT, |
955 | 5.40k | self.saturating_float_to_int_enabled, |
956 | | ); |
957 | 5.40k | features.set( |
958 | | WasmFeatures::SIGN_EXTENSION, |
959 | 5.40k | self.sign_extension_ops_enabled, |
960 | | ); |
961 | 5.40k | features.set(WasmFeatures::REFERENCE_TYPES, self.reference_types_enabled); |
962 | 5.40k | features.set(WasmFeatures::MULTI_VALUE, self.multi_value_enabled); |
963 | 5.40k | features.set(WasmFeatures::BULK_MEMORY, self.bulk_memory_enabled); |
964 | 5.40k | features.set(WasmFeatures::SIMD, self.simd_enabled); |
965 | 5.40k | features.set(WasmFeatures::RELAXED_SIMD, self.relaxed_simd_enabled); |
966 | 5.40k | features.set(WasmFeatures::MULTI_MEMORY, self.max_memories > 1); |
967 | 5.40k | features.set(WasmFeatures::EXCEPTIONS, self.exceptions_enabled); |
968 | 5.40k | features.set(WasmFeatures::MEMORY64, self.memory64_enabled); |
969 | 5.40k | features.set(WasmFeatures::TAIL_CALL, self.tail_call_enabled); |
970 | 5.40k | features.set(WasmFeatures::FUNCTION_REFERENCES, self.gc_enabled); |
971 | 5.40k | features.set(WasmFeatures::GC, self.gc_enabled); |
972 | 5.40k | features.set(WasmFeatures::THREADS, self.threads_enabled); |
973 | 5.40k | features.set( |
974 | | WasmFeatures::SHARED_EVERYTHING_THREADS, |
975 | 5.40k | self.shared_everything_threads_enabled, |
976 | | ); |
977 | 5.40k | features.set( |
978 | | WasmFeatures::CUSTOM_PAGE_SIZES, |
979 | 5.40k | self.custom_page_sizes_enabled, |
980 | | ); |
981 | 5.40k | features.set(WasmFeatures::EXTENDED_CONST, self.extended_const_enabled); |
982 | 5.40k | features.set(WasmFeatures::WIDE_ARITHMETIC, self.wide_arithmetic_enabled); |
983 | 5.40k | features.set( |
984 | | WasmFeatures::CUSTOM_DESCRIPTORS, |
985 | 5.40k | self.custom_descriptors_enabled, |
986 | | ); |
987 | | |
988 | 5.40k | features |
989 | 5.40k | } |
990 | | } |
991 | | |
992 | | #[cfg(feature = "serde")] |
993 | | impl<'de> serde::Deserialize<'de> for Config { |
994 | | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
995 | | where |
996 | | D: serde::de::Deserializer<'de>, |
997 | | { |
998 | | use serde::de::Error; |
999 | | |
1000 | | match Config::try_from(InternalOptionalConfig::deserialize(deserializer)?) { |
1001 | | Ok(config) => Ok(config), |
1002 | | Err(e) => Err(D::Error::custom(e)), |
1003 | | } |
1004 | | } |
1005 | | } |
1006 | | |
1007 | | #[cfg(feature = "serde")] |
1008 | | impl serde::Serialize for Config { |
1009 | | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
1010 | | where |
1011 | | S: serde::Serializer, |
1012 | | { |
1013 | | use serde::ser::Error; |
1014 | | |
1015 | | match InternalOptionalConfig::try_from(self) { |
1016 | | Ok(result) => result.serialize(serializer), |
1017 | | Err(e) => Err(S::Error::custom(e)), |
1018 | | } |
1019 | | } |
1020 | | } |