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}