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