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            /// 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            fn default() -> Config {
151                Config {
152                    available_imports: None,
153                    exports: None,
154                    module_shape: None,
155
156                    $(
157                        $field: $default,
158                    )*
159                }
160            }
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            pub fn or(self, other: Self) -> Self {
252                Self {
253                    available_imports: self.available_imports.or(other.available_imports),
254                    exports: self.exports.or(other.exports),
255                    module_shape: self.module_shape.or(other.module_shape),
256
257                    $(
258                        $field: self.$field.or(other.$field),
259                    )*
260                }
261            }
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            fn try_from(config: &Config) -> anyhow::Result<InternalOptionalConfig> {
302                if config.available_imports.is_some() {
303                    bail!("cannot serialize configuration with `available_imports`");
304                }
305                if config.exports.is_some() {
306                    bail!("cannot serialize configuration with `exports`");
307                }
308                if config.module_shape.is_some() {
309                    bail!("cannot serialize configuration with `module_shape`");
310                }
311                Ok(InternalOptionalConfig {
312                    available_imports: None,
313                    exports: None,
314                    module_shape: None,
315                    $( $field: Some(config.$field.clone()), )*
316                })
317            }
318        }
319    }
320}
321
322define_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)]
769pub struct MemoryOffsetChoices(pub u32, pub u32, pub u32);
770
771impl Default for MemoryOffsetChoices {
772    fn default() -> Self {
773        MemoryOffsetChoices(90, 9, 1)
774    }
775}
776
777impl std::str::FromStr for MemoryOffsetChoices {
778    type Err = String;
779    fn from_str(s: &str) -> Result<Self, Self::Err> {
780        use std::str::FromStr;
781        let mut parts = s.split(",");
782        let a = parts
783            .next()
784            .ok_or_else(|| "need 3 comma separated values".to_string())?;
785        let a = <u32 as FromStr>::from_str(a).map_err(|e| e.to_string())?;
786        let b = parts
787            .next()
788            .ok_or_else(|| "need 3 comma separated values".to_string())?;
789        let b = <u32 as FromStr>::from_str(b).map_err(|e| e.to_string())?;
790        let c = parts
791            .next()
792            .ok_or_else(|| "need 3 comma separated values".to_string())?;
793        let c = <u32 as FromStr>::from_str(c).map_err(|e| e.to_string())?;
794        if parts.next().is_some() {
795            return Err("found more than 3 comma separated values".to_string());
796        }
797        Ok(MemoryOffsetChoices(a, b, c))
798    }
799}
800
801impl<'a> Arbitrary<'a> for Config {
802    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
803        const MAX_MAXIMUM: usize = 1000;
804
805        let mut config = Config {
806            max_types: u.int_in_range(0..=MAX_MAXIMUM)?,
807            max_imports: u.int_in_range(0..=MAX_MAXIMUM)?,
808            max_tags: u.int_in_range(0..=MAX_MAXIMUM)?,
809            max_funcs: u.int_in_range(0..=MAX_MAXIMUM)?,
810            max_globals: u.int_in_range(0..=MAX_MAXIMUM)?,
811            max_exports: u.int_in_range(0..=MAX_MAXIMUM)?,
812            max_element_segments: u.int_in_range(0..=MAX_MAXIMUM)?,
813            max_elements: u.int_in_range(0..=MAX_MAXIMUM)?,
814            max_data_segments: u.int_in_range(0..=MAX_MAXIMUM)?,
815            max_instructions: u.int_in_range(0..=MAX_MAXIMUM)?,
816            max_memories: u.int_in_range(0..=100)?,
817            max_tables: u.int_in_range(0..=100)?,
818            max_memory32_bytes: u.int_in_range(0..=u32::MAX as u64 + 1)?,
819            max_memory64_bytes: u.int_in_range(0..=u64::MAX as u128 + 1)?,
820            min_uleb_size: u.int_in_range(0..=5)?,
821            bulk_memory_enabled: u.arbitrary()?,
822            reference_types_enabled: u.arbitrary()?,
823            simd_enabled: u.arbitrary()?,
824            multi_value_enabled: u.arbitrary()?,
825            max_aliases: u.int_in_range(0..=MAX_MAXIMUM)?,
826            max_nesting_depth: u.int_in_range(0..=10)?,
827            saturating_float_to_int_enabled: u.arbitrary()?,
828            sign_extension_ops_enabled: u.arbitrary()?,
829            relaxed_simd_enabled: u.arbitrary()?,
830            exceptions_enabled: u.arbitrary()?,
831            threads_enabled: u.arbitrary()?,
832            tail_call_enabled: u.arbitrary()?,
833            gc_enabled: u.arbitrary()?,
834            memory64_enabled: u.arbitrary()?,
835            allowed_instructions: {
836                use flagset::Flags;
837                let mut allowed = Vec::new();
838                for kind in crate::core::InstructionKind::LIST {
839                    if u.arbitrary()? {
840                        allowed.push(*kind);
841                    }
842                }
843                InstructionKinds::new(&allowed)
844            },
845            table_max_size_required: u.arbitrary()?,
846            max_table_elements: u.int_in_range(0..=1_000_000)?,
847            disallow_traps: u.arbitrary()?,
848            allow_floats: u.arbitrary()?,
849            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            memory_offset_choices: MemoryOffsetChoices::default(),
871            allow_start_export: true,
872            max_type_size: 1000,
873            canonicalize_nans: false,
874            available_imports: None,
875            exports: None,
876            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        config.sanitize();
888        Ok(config)
889    }
890}
891
892impl 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    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        if !self.reference_types_enabled {
909            self.max_tables = self.max_tables.min(1);
910            self.gc_enabled = false;
911            self.shared_everything_threads_enabled = false;
912        }
913
914        // shared-everything-threads depends on GC, so if gc is disabled then
915        // also disable shared-everything-threads.
916        if !self.gc_enabled {
917            self.shared_everything_threads_enabled = false;
918            self.custom_descriptors_enabled = false;
919        }
920
921        // If simd is disabled then disable all relaxed simd instructions as
922        // well.
923        if !self.simd_enabled {
924            self.relaxed_simd_enabled = false;
925        }
926
927        // It is impossible to use the shared-everything-threads proposal
928        // without threads, which it is built on.
929        if !self.threads_enabled {
930            self.shared_everything_threads_enabled = false;
931        }
932
933        // If module_shape is present then disable available_imports and exports.
934        if self.module_shape.is_some() {
935            self.available_imports = None;
936            self.exports = None;
937        }
938    }
939
940    /// Returns the set of features that are necessary for validating against
941    /// this `Config`.
942    #[cfg(feature = "wasmparser")]
943    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        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        features.set(
954            WasmFeatures::SATURATING_FLOAT_TO_INT,
955            self.saturating_float_to_int_enabled,
956        );
957        features.set(
958            WasmFeatures::SIGN_EXTENSION,
959            self.sign_extension_ops_enabled,
960        );
961        features.set(WasmFeatures::REFERENCE_TYPES, self.reference_types_enabled);
962        features.set(WasmFeatures::MULTI_VALUE, self.multi_value_enabled);
963        features.set(WasmFeatures::BULK_MEMORY, self.bulk_memory_enabled);
964        features.set(WasmFeatures::SIMD, self.simd_enabled);
965        features.set(WasmFeatures::RELAXED_SIMD, self.relaxed_simd_enabled);
966        features.set(WasmFeatures::MULTI_MEMORY, self.max_memories > 1);
967        features.set(WasmFeatures::EXCEPTIONS, self.exceptions_enabled);
968        features.set(WasmFeatures::MEMORY64, self.memory64_enabled);
969        features.set(WasmFeatures::TAIL_CALL, self.tail_call_enabled);
970        features.set(WasmFeatures::FUNCTION_REFERENCES, self.gc_enabled);
971        features.set(WasmFeatures::GC, self.gc_enabled);
972        features.set(WasmFeatures::THREADS, self.threads_enabled);
973        features.set(
974            WasmFeatures::SHARED_EVERYTHING_THREADS,
975            self.shared_everything_threads_enabled,
976        );
977        features.set(
978            WasmFeatures::CUSTOM_PAGE_SIZES,
979            self.custom_page_sizes_enabled,
980        );
981        features.set(WasmFeatures::EXTENDED_CONST, self.extended_const_enabled);
982        features.set(WasmFeatures::WIDE_ARITHMETIC, self.wide_arithmetic_enabled);
983        features.set(
984            WasmFeatures::CUSTOM_DESCRIPTORS,
985            self.custom_descriptors_enabled,
986        );
987
988        features
989    }
990}
991
992#[cfg(feature = "serde")]
993impl<'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")]
1008impl 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}