diff --git a/Cargo.toml b/Cargo.toml index b8b67dde6..fa867df0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ rust_decimal = { version = "1.16.0", default-features = false, features = ["math getrandom = { version = "0.2.0", optional = true } rustyline = { version = "12.0.0", optional = true } document-features = { version = "0.2.0", optional = true } +arbitrary = { version = "1.3.2", optional = true, features = ["derive"] } [dev-dependencies] rmp-serde = "1.1.0" diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 000000000..1a45eee77 --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 000000000..4a7aa68e8 --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "rhai-fuzz" +version = "0.0.0" +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +arbitrary = { version = "1.3.2", features = ["derive"] } +libfuzzer-sys = "0.4" + +[dependencies.rhai] +path = ".." +features = ["arbitrary"] + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "scripting" +path = "fuzz_targets/scripting.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/scripting.rs b/fuzz/fuzz_targets/scripting.rs new file mode 100644 index 000000000..4440ac8e2 --- /dev/null +++ b/fuzz/fuzz_targets/scripting.rs @@ -0,0 +1,36 @@ +#![no_main] +use rhai::{Dynamic, Engine, OptimizationLevel}; + +use arbitrary::Arbitrary; +use libfuzzer_sys::fuzz_target; +use std::time::{Duration, Instant}; + +#[derive(Debug, Clone, Arbitrary)] +struct Ctx<'a> { + script: &'a str, + optimization_level: OptimizationLevel, +} + +fuzz_target!(|ctx: Ctx| { + let mut engine = Engine::new(); + engine.set_max_string_size(1000); + engine.set_max_array_size(500); + engine.set_max_map_size(500); + engine.set_max_variables(1000); + engine.set_max_modules(1000); + engine.set_max_call_levels(10); + engine.set_max_expr_depths(50, 5); + engine.set_optimization_level(ctx.optimization_level); + let start = Instant::now(); + engine.on_progress(move |_| { + // We need fuzzing to be fast, so we'll stop executing after 1s. + if start.elapsed() > Duration::from_secs(1) { + Some(Dynamic::UNIT) + } else { + None + } + }); + let engine = engine; + + _ = engine.run(ctx.script); +}); diff --git a/src/optimizer.rs b/src/optimizer.rs index 28ffb9614..cf723ffe5 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -31,6 +31,7 @@ use std::{ /// /// Not available under `no_optimize`. #[derive(Debug, Eq, PartialEq, Clone, Copy, Default, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[non_exhaustive] pub enum OptimizationLevel { /// No optimization performed.