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