Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/fuzz/build.rs
1685 views
1
fn main() -> anyhow::Result<()> {
2
component::generate_static_api_tests()?;
3
4
Ok(())
5
}
6
7
mod component {
8
use anyhow::{Context, Error, Result, anyhow};
9
use arbitrary::Unstructured;
10
use proc_macro2::TokenStream;
11
use quote::quote;
12
use rand::rngs::StdRng;
13
use rand::{Rng, SeedableRng};
14
use std::env;
15
use std::fmt::Write;
16
use std::fs;
17
use std::iter;
18
use std::path::PathBuf;
19
use std::process::Command;
20
use wasmtime_test_util::component_fuzz::{Declarations, MAX_TYPE_DEPTH, TestCase, Type};
21
22
pub fn generate_static_api_tests() -> Result<()> {
23
println!("cargo:rerun-if-changed=build.rs");
24
let out_dir = PathBuf::from(
25
env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"),
26
);
27
28
let mut out = String::new();
29
write_static_api_tests(&mut out)?;
30
31
let output = out_dir.join("static_component_api.rs");
32
fs::write(&output, out)?;
33
34
drop(Command::new("rustfmt").arg(&output).status());
35
36
Ok(())
37
}
38
39
fn write_static_api_tests(out: &mut String) -> Result<()> {
40
let seed = if let Ok(seed) = env::var("WASMTIME_FUZZ_SEED") {
41
seed.parse::<u64>()
42
.with_context(|| anyhow!("expected u64 in WASMTIME_FUZZ_SEED"))?
43
} else {
44
StdRng::from_entropy().r#gen()
45
};
46
47
eprintln!(
48
"using seed {seed} (set WASMTIME_FUZZ_SEED={seed} in your environment to reproduce)"
49
);
50
51
let mut rng = StdRng::seed_from_u64(seed);
52
53
const TYPE_COUNT: usize = 50;
54
const MAX_ARITY: u32 = 5;
55
const TEST_CASE_COUNT: usize = 100;
56
57
let mut type_fuel = 1000;
58
let mut types = Vec::new();
59
let name_counter = &mut 0;
60
let mut declarations = TokenStream::new();
61
let mut tests = TokenStream::new();
62
63
// First generate a set of type to select from.
64
for _ in 0..TYPE_COUNT {
65
let ty = generate(&mut rng, |u| {
66
// Only discount fuel if the generation was successful,
67
// otherwise we'll get more random data and try again.
68
let mut fuel = type_fuel;
69
let ret = Type::generate(u, MAX_TYPE_DEPTH, &mut fuel);
70
if ret.is_ok() {
71
type_fuel = fuel;
72
}
73
ret
74
})?;
75
76
let name =
77
wasmtime_test_util::component_fuzz::rust_type(&ty, name_counter, &mut declarations);
78
types.push((name, ty));
79
}
80
81
// Next generate a set of static API test cases driven by the above
82
// types.
83
for index in 0..TEST_CASE_COUNT {
84
let (case, rust_params, rust_results) = generate(&mut rng, |u| {
85
let mut params = Vec::new();
86
let mut result = None;
87
let mut rust_params = TokenStream::new();
88
let mut rust_results = TokenStream::new();
89
for _ in 0..u.int_in_range(0..=MAX_ARITY)? {
90
let (name, ty) = u.choose(&types)?;
91
params.push(ty);
92
rust_params.extend(name.clone());
93
rust_params.extend(quote!(,));
94
}
95
if u.arbitrary()? {
96
let (name, ty) = u.choose(&types)?;
97
result = Some(ty);
98
rust_results.extend(name.clone());
99
rust_results.extend(quote!(,));
100
}
101
102
let case = TestCase {
103
params,
104
result,
105
encoding1: u.arbitrary()?,
106
encoding2: u.arbitrary()?,
107
};
108
Ok((case, rust_params, rust_results))
109
})?;
110
111
let Declarations {
112
types,
113
type_instantiation_args,
114
params,
115
results,
116
import_and_export,
117
encoding1,
118
encoding2,
119
} = case.declarations();
120
121
let test = quote!(#index => component_types::static_api_test::<(#rust_params), (#rust_results)>(
122
input,
123
{
124
static DECLS: Declarations = Declarations {
125
types: Cow::Borrowed(#types),
126
type_instantiation_args: Cow::Borrowed(#type_instantiation_args),
127
params: Cow::Borrowed(#params),
128
results: Cow::Borrowed(#results),
129
import_and_export: Cow::Borrowed(#import_and_export),
130
encoding1: #encoding1,
131
encoding2: #encoding2,
132
};
133
&DECLS
134
}
135
),);
136
137
tests.extend(test);
138
}
139
140
let module = quote! {
141
#[allow(unused_imports, reason = "macro-generated code")]
142
fn static_component_api_target(input: &mut libfuzzer_sys::arbitrary::Unstructured) -> libfuzzer_sys::arbitrary::Result<()> {
143
use anyhow::Result;
144
use wasmtime_test_util::component_fuzz::Declarations;
145
use wasmtime_test_util::component::{Float32, Float64};
146
use libfuzzer_sys::arbitrary::{self, Arbitrary};
147
use std::borrow::Cow;
148
use std::sync::{Arc, Once};
149
use wasmtime::component::{ComponentType, Lift, Lower};
150
use wasmtime_fuzzing::generators::component_types;
151
152
const SEED: u64 = #seed;
153
154
static ONCE: Once = Once::new();
155
156
ONCE.call_once(|| {
157
eprintln!(
158
"Seed {SEED} was used to generate static component API fuzz tests.\n\
159
Set WASMTIME_FUZZ_SEED={SEED} in your environment at build time to reproduce."
160
);
161
});
162
163
#declarations
164
165
match input.int_in_range(0..=(#TEST_CASE_COUNT-1))? {
166
#tests
167
_ => unreachable!()
168
}
169
}
170
};
171
172
write!(out, "{module}")?;
173
174
Ok(())
175
}
176
177
fn generate<T>(
178
rng: &mut StdRng,
179
mut f: impl FnMut(&mut Unstructured<'_>) -> arbitrary::Result<T>,
180
) -> Result<T> {
181
let mut bytes = Vec::new();
182
loop {
183
let count = rng.gen_range(1000..2000);
184
bytes.extend(iter::repeat_with(|| rng.r#gen::<u8>()).take(count));
185
186
match f(&mut Unstructured::new(&bytes)) {
187
Ok(ret) => break Ok(ret),
188
Err(arbitrary::Error::NotEnoughData) => (),
189
Err(error) => break Err(Error::from(error)),
190
}
191
}
192
}
193
}
194
195