Coverage Report

Created: 2026-10-12 08:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm-tools/crates/wasm-smith/src/config.rs
Line
Count
Source
1
//! Configuring the shape of generated Wasm modules.
2
3
use crate::InstructionKinds;
4
use anyhow::bail;
5
use arbitrary::{Arbitrary, Result, Unstructured};
6
7
macro_rules! define_config {
8
    (
9
        $(#[$attr:meta])*
10
        pub struct Config {
11
            $(
12
                $(#[$field_attr:meta])*
13
                pub $field:ident : $field_ty:ty = $default:expr,
14
            )*
15
        }
16
    ) => {
17
        $(#[$attr])*
18
        pub struct Config {
19
            /// The imports that may be used when generating the module.
20
            ///
21
            /// Defaults to `None` which means that any arbitrary import can be
22
            /// generated.
23
            ///
24
            /// To only allow specific imports, set this field to a WebAssembly
25
            /// module which describes the imports allowed.
26
            ///
27
            /// Note that [`Self::min_imports`] is ignored when
28
            /// `available_imports` are enabled.
29
            ///
30
            /// The provided value must be a valid binary encoding of a
31
            /// WebAssembly module. `wasm-smith` will panic if the module cannot
32
            /// be parsed.
33
            ///
34
            /// # Example
35
            ///
36
            /// An implementation of this method could use the `wat` crate to
37
            /// provide a human-readable and maintainable description:
38
            ///
39
            /// ```rust
40
            /// Some(wat::parse_str(r#"
41
            ///     (module
42
            ///         (import "env" "ping" (func (param i32)))
43
            ///         (import "env" "pong" (func (result i32)))
44
            ///         (import "env" "memory" (memory 1))
45
            ///         (import "env" "table" (table 1))
46
            ///         (import "env" "tag" (tag (param i32)))
47
            ///     )
48
            /// "#))
49
            /// # ;
50
            /// ```
51
            pub available_imports: Option<Vec<u8>>,
52
53
            /// If provided, the generated module will have exports with exactly
54
            /// the same names and types as those in the provided WebAssembly
55
            /// module. The implementation (e.g. function bodies, global
56
            /// initializers) of each export in the generated module will be
57
            /// random and unrelated to the implementation in the provided
58
            /// module.
59
            ///
60
            ///
61
            /// Defaults to `None` which means arbitrary exports will be
62
            /// generated.
63
            ///
64
            /// To specify which exports the generated modules should have, set
65
            /// this field to a WebAssembly module which describes the desired
66
            /// exports. To generate modules with varying exports that meet some
67
            /// constraints, consider randomly generating the value for this
68
            /// field.
69
            ///
70
            /// The provided value must be a valid binary encoding of a
71
            /// WebAssembly module. `wasm-smith` will panic if the module cannot
72
            /// be parsed.
73
            ///
74
            /// # Module Limits
75
            ///
76
            /// All types, functions, globals, memories, tables, tags, and exports
77
            /// that are needed to provide the required exports will be generated,
78
            /// even if it causes the resulting module to exceed the limits defined
79
            /// in [`Self::max_type_size`], [`Self::max_types`],
80
            /// [`Self::max_funcs`], [`Self::max_globals`],
81
            /// [`Self::max_memories`], [`Self::max_tables`],
82
            /// [`Self::max_tags`], or [`Self::max_exports`].
83
            ///
84
            /// # Example
85
            ///
86
            /// As for [`Self::available_imports`], the `wat` crate can be used
87
            /// to provide an human-readable description of the desired exports:
88
            ///
89
            /// ```rust
90
            /// Some(wat::parse_str(r#"
91
            ///     (module
92
            ///         (func (export "foo") (param i32) (result i64) unreachable)
93
            ///         (global (export "bar") f32 f32.const 0)
94
            ///         (memory (export "baz") 1 10)
95
            ///         (table (export "qux") 5 10 (ref null extern))
96
            ///         (tag (export "quux") (param f32))
97
            ///     )
98
            /// "#));
99
            /// ```
100
            pub exports: Option<Vec<u8>>,
101
102
            /// If provided, the generated module will have imports and exports
103
            /// with exactly the same names and types as those in the provided
104
            /// WebAssembly module.
105
            ///
106
            /// 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
0
            fn default() -> Config {
157
0
                Config {
158
0
                    available_imports: None,
159
0
                    exports: None,
160
0
                    module_shape: None,
161
0
162
0
                    $(
163
0
                        $field: $default,
164
0
                    )*
165
0
                }
166
0
            }
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
0
            pub fn or(self, other: Self) -> Self {
264
0
                Self {
265
0
                    available_imports: self.available_imports.or(other.available_imports),
266
0
                    exports: self.exports.or(other.exports),
267
0
                    module_shape: self.module_shape.or(other.module_shape),
268
0
269
0
                    $(
270
0
                        $field: self.$field.or(other.$field),
271
0
                    )*
272
0
                }
273
0
            }
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
0
            fn try_from(config: &Config) -> anyhow::Result<InternalOptionalConfig> {
314
0
                if config.available_imports.is_some() {
315
0
                    bail!("cannot serialize configuration with `available_imports`");
316
0
                }
317
0
                if config.exports.is_some() {
318
0
                    bail!("cannot serialize configuration with `exports`");
319
0
                }
320
0
                if config.module_shape.is_some() {
321
0
                    bail!("cannot serialize configuration with `module_shape`");
322
0
                }
323
0
                Ok(InternalOptionalConfig {
324
0
                    available_imports: None,
325
0
                    exports: None,
326
0
                    module_shape: None,
327
0
                    $( $field: Some(config.$field.clone()), )*
328
0
                })
329
0
            }
330
        }
331
    }
332
}
333
334
define_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
)]
797
pub struct MemoryOffsetChoices(pub u32, pub u32, pub u32);
798
799
impl Default for MemoryOffsetChoices {
800
10.9k
    fn default() -> Self {
801
10.9k
        MemoryOffsetChoices(90, 9, 1)
802
10.9k
    }
803
}
804
805
impl std::str::FromStr for MemoryOffsetChoices {
806
    type Err = String;
807
0
    fn from_str(s: &str) -> Result<Self, Self::Err> {
808
        use std::str::FromStr;
809
0
        let mut parts = s.split(",");
810
0
        let a = parts
811
0
            .next()
812
0
            .ok_or_else(|| "need 3 comma separated values".to_string())?;
813
0
        let a = <u32 as FromStr>::from_str(a).map_err(|e| e.to_string())?;
814
0
        let b = parts
815
0
            .next()
816
0
            .ok_or_else(|| "need 3 comma separated values".to_string())?;
817
0
        let b = <u32 as FromStr>::from_str(b).map_err(|e| e.to_string())?;
818
0
        let c = parts
819
0
            .next()
820
0
            .ok_or_else(|| "need 3 comma separated values".to_string())?;
821
0
        let c = <u32 as FromStr>::from_str(c).map_err(|e| e.to_string())?;
822
0
        if parts.next().is_some() {
823
0
            return Err("found more than 3 comma separated values".to_string());
824
0
        }
825
0
        Ok(MemoryOffsetChoices(a, b, c))
826
0
    }
827
}
828
829
impl<'a> Arbitrary<'a> for Config {
830
10.9k
    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
831
        const MAX_MAXIMUM: usize = 1000;
832
833
10.9k
        let mut config = Config {
834
10.9k
            max_types: u.int_in_range(0..=MAX_MAXIMUM)?,
835
10.9k
            max_imports: u.int_in_range(0..=MAX_MAXIMUM)?,
836
10.9k
            max_tags: u.int_in_range(0..=MAX_MAXIMUM)?,
837
10.9k
            max_funcs: u.int_in_range(0..=MAX_MAXIMUM)?,
838
10.9k
            max_globals: u.int_in_range(0..=MAX_MAXIMUM)?,
839
10.9k
            max_exports: u.int_in_range(0..=MAX_MAXIMUM)?,
840
10.9k
            max_element_segments: u.int_in_range(0..=MAX_MAXIMUM)?,
841
10.9k
            max_elements: u.int_in_range(0..=MAX_MAXIMUM)?,
842
10.9k
            max_data_segments: u.int_in_range(0..=MAX_MAXIMUM)?,
843
10.9k
            max_instructions: u.int_in_range(0..=MAX_MAXIMUM)?,
844
10.9k
            max_memories: u.int_in_range(0..=100)?,
845
10.9k
            max_tables: u.int_in_range(0..=100)?,
846
10.9k
            max_memory32_bytes: u.int_in_range(0..=u32::MAX as u64 + 1)?,
847
10.9k
            max_memory64_bytes: u.int_in_range(0..=u64::MAX as u128 + 1)?,
848
10.9k
            min_uleb_size: u.int_in_range(0..=5)?,
849
10.9k
            bulk_memory_enabled: u.arbitrary()?,
850
10.9k
            reference_types_enabled: u.arbitrary()?,
851
10.9k
            simd_enabled: u.arbitrary()?,
852
10.9k
            multi_value_enabled: u.arbitrary()?,
853
10.9k
            max_aliases: u.int_in_range(0..=MAX_MAXIMUM)?,
854
10.9k
            max_nesting_depth: u.int_in_range(0..=10)?,
855
10.9k
            saturating_float_to_int_enabled: u.arbitrary()?,
856
10.9k
            sign_extension_ops_enabled: u.arbitrary()?,
857
10.9k
            relaxed_simd_enabled: u.arbitrary()?,
858
10.9k
            exceptions_enabled: u.arbitrary()?,
859
10.9k
            threads_enabled: u.arbitrary()?,
860
10.9k
            tail_call_enabled: u.arbitrary()?,
861
10.9k
            gc_enabled: u.arbitrary()?,
862
10.9k
            compact_imports_enabled: u.arbitrary()?,
863
10.9k
            memory64_enabled: u.arbitrary()?,
864
            allowed_instructions: {
865
                use flagset::Flags;
866
10.9k
                let mut allowed = Vec::new();
867
131k
                for kind in crate::core::InstructionKind::LIST {
868
131k
                    if u.arbitrary()? {
869
88.1k
                        allowed.push(*kind);
870
88.1k
                    }
871
                }
872
10.9k
                InstructionKinds::new(&allowed)
873
            },
874
10.9k
            table_max_size_required: u.arbitrary()?,
875
10.9k
            max_table_elements: u.int_in_range(0..=1_000_000)?,
876
10.9k
            disallow_traps: u.arbitrary()?,
877
10.9k
            allow_floats: u.arbitrary()?,
878
10.9k
            extended_const_enabled: u.arbitrary()?,
879
10.9k
            const_expr_fuel: u.int_in_range(0..=100)?,
880
10.9k
            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
10.9k
            memory_offset_choices: MemoryOffsetChoices::default(),
902
            allow_start_export: true,
903
            max_type_size: 1000,
904
            canonicalize_nans: false,
905
10.9k
            available_imports: None,
906
10.9k
            exports: None,
907
10.9k
            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
10.9k
        config.sanitize();
919
10.9k
        Ok(config)
920
10.9k
    }
921
}
922
923
impl 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
21.8k
    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
21.8k
        if !self.reference_types_enabled {
940
9.15k
            self.max_tables = self.max_tables.min(1);
941
9.15k
            self.gc_enabled = false;
942
9.15k
            self.shared_everything_threads_enabled = false;
943
12.7k
        }
944
945
        // shared-everything-threads depends on GC, so if gc is disabled then
946
        // also disable shared-everything-threads.
947
21.8k
        if !self.gc_enabled {
948
14.4k
            self.shared_everything_threads_enabled = false;
949
14.4k
            self.custom_descriptors_enabled = false;
950
14.4k
        }
951
952
        // If simd is disabled then disable all relaxed simd instructions as
953
        // well.
954
21.8k
        if !self.simd_enabled {
955
5.84k
            self.relaxed_simd_enabled = false;
956
16.0k
        }
957
958
        // It is impossible to use the shared-everything-threads proposal
959
        // without threads, which it is built on.
960
21.8k
        if !self.threads_enabled {
961
9.14k
            self.shared_everything_threads_enabled = false;
962
12.7k
        }
963
964
        // If module_shape is present then disable available_imports and exports.
965
21.8k
        if self.module_shape.is_some() {
966
0
            self.available_imports = None;
967
0
            self.exports = None;
968
21.8k
        }
969
21.8k
    }
970
971
    /// Returns the set of features that are necessary for validating against
972
    /// this `Config`.
973
    #[cfg(feature = "wasmparser")]
974
3.96k
    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
3.96k
        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
3.96k
        features.set(
985
            WasmFeatures::SATURATING_FLOAT_TO_INT,
986
3.96k
            self.saturating_float_to_int_enabled,
987
        );
988
3.96k
        features.set(
989
            WasmFeatures::SIGN_EXTENSION,
990
3.96k
            self.sign_extension_ops_enabled,
991
        );
992
3.96k
        features.set(WasmFeatures::REFERENCE_TYPES, self.reference_types_enabled);
993
3.96k
        features.set(WasmFeatures::MULTI_VALUE, self.multi_value_enabled);
994
3.96k
        features.set(WasmFeatures::BULK_MEMORY, self.bulk_memory_enabled);
995
3.96k
        features.set(WasmFeatures::SIMD, self.simd_enabled);
996
3.96k
        features.set(WasmFeatures::RELAXED_SIMD, self.relaxed_simd_enabled);
997
3.96k
        features.set(WasmFeatures::MULTI_MEMORY, self.max_memories > 1);
998
3.96k
        features.set(WasmFeatures::EXCEPTIONS, self.exceptions_enabled);
999
3.96k
        features.set(WasmFeatures::MEMORY64, self.memory64_enabled);
1000
3.96k
        features.set(WasmFeatures::TAIL_CALL, self.tail_call_enabled);
1001
3.96k
        features.set(WasmFeatures::FUNCTION_REFERENCES, self.gc_enabled);
1002
3.96k
        features.set(WasmFeatures::GC, self.gc_enabled);
1003
3.96k
        features.set(WasmFeatures::COMPACT_IMPORTS, self.compact_imports_enabled);
1004
3.96k
        features.set(WasmFeatures::THREADS, self.threads_enabled);
1005
3.96k
        features.set(
1006
            WasmFeatures::SHARED_EVERYTHING_THREADS,
1007
3.96k
            self.shared_everything_threads_enabled,
1008
        );
1009
3.96k
        features.set(
1010
            WasmFeatures::CUSTOM_PAGE_SIZES,
1011
3.96k
            self.custom_page_sizes_enabled,
1012
        );
1013
3.96k
        features.set(WasmFeatures::EXTENDED_CONST, self.extended_const_enabled);
1014
3.96k
        features.set(WasmFeatures::WIDE_ARITHMETIC, self.wide_arithmetic_enabled);
1015
3.96k
        features.set(
1016
            WasmFeatures::CUSTOM_DESCRIPTORS,
1017
3.96k
            self.custom_descriptors_enabled,
1018
        );
1019
1020
3.96k
        features
1021
3.96k
    }
1022
}
1023
1024
#[cfg(feature = "serde")]
1025
impl<'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")]
1040
impl 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
}