summaryrefslogtreecommitdiffstats
path: root/third_party/rust/wasm-smith/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/wasm-smith/src/lib.rs')
-rw-r--r--third_party/rust/wasm-smith/src/lib.rs193
1 files changed, 193 insertions, 0 deletions
diff --git a/third_party/rust/wasm-smith/src/lib.rs b/third_party/rust/wasm-smith/src/lib.rs
new file mode 100644
index 0000000000..63db1ad93c
--- /dev/null
+++ b/third_party/rust/wasm-smith/src/lib.rs
@@ -0,0 +1,193 @@
+//! A WebAssembly test case generator.
+//!
+//! ## Usage
+//!
+//! First, use [`cargo fuzz`](https://github.com/rust-fuzz/cargo-fuzz) to define
+//! a new fuzz target:
+//!
+//! ```shell
+//! $ cargo fuzz add my_wasm_smith_fuzz_target
+//! ```
+//!
+//! Next, add `wasm-smith` to your dependencies:
+//!
+//! ```shell
+//! $ cargo add wasm-smith
+//! ```
+//!
+//! Then, define your fuzz target so that it takes arbitrary
+//! `wasm_smith::Module`s as an argument, convert the module into serialized
+//! Wasm bytes via the `to_bytes` method, and then feed it into your system:
+//!
+//! ```no_run
+//! // fuzz/fuzz_targets/my_wasm_smith_fuzz_target.rs
+//!
+//! #![no_main]
+//!
+//! use libfuzzer_sys::fuzz_target;
+//! use wasm_smith::Module;
+//!
+//! fuzz_target!(|module: Module| {
+//! let wasm_bytes = module.to_bytes();
+//!
+//! // Your code here...
+//! });
+//! ```
+//!
+//! Finally, start fuzzing:
+//!
+//! ```shell
+//! $ cargo fuzz run my_wasm_smith_fuzz_target
+//! ```
+//!
+//! > **Note:** For a real world example, also check out [the `validate` fuzz
+//! > target](https://github.com/fitzgen/wasm-smith/blob/main/fuzz/fuzz_targets/validate.rs)
+//! > defined in this repository. Using the `wasmparser` crate, it checks that
+//! > every module generated by `wasm-smith` validates successfully.
+//!
+//! ## Design
+//!
+//! The design and implementation strategy of wasm-smith is outlined in
+//! [this article](https://fitzgeraldnick.com/2020/08/24/writing-a-test-case-generator.html).
+
+#![deny(missing_docs, missing_debug_implementations)]
+// Needed for the `instructions!` macro in `src/code_builder.rs`.
+#![recursion_limit = "512"]
+
+mod component;
+mod config;
+mod core;
+
+pub use crate::core::{
+ ConfiguredModule, InstructionKind, InstructionKinds, MaybeInvalidModule, Module,
+};
+use arbitrary::{Result, Unstructured};
+pub use component::{Component, ConfiguredComponent};
+pub use config::{Config, DefaultConfig, SwarmConfig};
+use std::{collections::HashSet, fmt::Write, str};
+use wasmparser::types::{KebabStr, KebabString};
+
+/// Do something an arbitrary number of times.
+///
+/// The callback can return `false` to exit the loop early.
+pub(crate) fn arbitrary_loop<'a>(
+ u: &mut Unstructured<'a>,
+ min: usize,
+ max: usize,
+ mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
+) -> Result<()> {
+ assert!(max >= min);
+ for _ in 0..min {
+ if !f(u)? {
+ return Err(arbitrary::Error::IncorrectFormat);
+ }
+ }
+ for _ in 0..(max - min) {
+ let keep_going = u.arbitrary().unwrap_or(false);
+ if !keep_going {
+ break;
+ }
+
+ if !f(u)? {
+ break;
+ }
+ }
+
+ Ok(())
+}
+
+// Mirror what happens in `Arbitrary for String`, but do so with a clamped size.
+pub(crate) fn limited_str<'a>(max_size: usize, u: &mut Unstructured<'a>) -> Result<&'a str> {
+ let size = u.arbitrary_len::<u8>()?;
+ let size = std::cmp::min(size, max_size);
+ match str::from_utf8(u.peek_bytes(size).unwrap()) {
+ Ok(s) => {
+ u.bytes(size).unwrap();
+ Ok(s)
+ }
+ Err(e) => {
+ let i = e.valid_up_to();
+ let valid = u.bytes(i).unwrap();
+ let s = unsafe {
+ debug_assert!(str::from_utf8(valid).is_ok());
+ str::from_utf8_unchecked(valid)
+ };
+ Ok(s)
+ }
+ }
+}
+
+pub(crate) fn limited_string(max_size: usize, u: &mut Unstructured) -> Result<String> {
+ Ok(limited_str(max_size, u)?.into())
+}
+
+pub(crate) fn unique_string(
+ max_size: usize,
+ names: &mut HashSet<String>,
+ u: &mut Unstructured,
+) -> Result<String> {
+ let mut name = limited_string(max_size, u)?;
+ while names.contains(&name) {
+ write!(&mut name, "{}", names.len()).unwrap();
+ }
+ names.insert(name.clone());
+ Ok(name)
+}
+
+pub(crate) fn unique_kebab_string(
+ max_size: usize,
+ names: &mut HashSet<KebabString>,
+ u: &mut Unstructured,
+) -> Result<KebabString> {
+ let size = std::cmp::min(u.arbitrary_len::<u8>()?, max_size);
+ let mut name = String::with_capacity(size);
+ let mut require_alpha = true;
+ for _ in 0..size {
+ name.push(match u.int_in_range::<u8>(0..=36)? {
+ x if (0..26).contains(&x) => {
+ require_alpha = false;
+ (b'a' + x) as char
+ }
+ x if (26..36).contains(&x) => {
+ if require_alpha {
+ require_alpha = false;
+ (b'a' + (x - 26)) as char
+ } else {
+ (b'0' + (x - 26)) as char
+ }
+ }
+ x if x == 36 => {
+ if require_alpha {
+ require_alpha = false;
+ 'a'
+ } else {
+ require_alpha = true;
+ '-'
+ }
+ }
+ _ => unreachable!(),
+ });
+ }
+
+ if name.is_empty() || name.ends_with('-') {
+ name.push('a');
+ }
+
+ while names.contains(KebabStr::new(&name).unwrap()) {
+ write!(&mut name, "{}", names.len()).unwrap();
+ }
+
+ let name = KebabString::new(name).unwrap();
+ names.insert(name.clone());
+
+ Ok(name)
+}
+
+pub(crate) fn unique_url(
+ max_size: usize,
+ names: &mut HashSet<KebabString>,
+ u: &mut Unstructured,
+) -> Result<String> {
+ let path = unique_kebab_string(max_size, names, u)?;
+ Ok(format!("https://example.com/{path}"))
+}