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