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