Skip to main content

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}