Add fuzzing based on serde_json
This test ensures that we can parse anything that serde_json can produce, which *ought* to ensure reasonable coverage?
This commit is contained in:
parent
43f6b75762
commit
35dcf93971
3 changed files with 126 additions and 0 deletions
40
fuzz/Cargo.lock
generated
40
fuzz/Cargo.lock
generated
|
|
@ -43,6 +43,9 @@ name = "arbitrary"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||||
|
dependencies = [
|
||||||
|
"derive_arbitrary",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
|
|
@ -247,6 +250,17 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
|
checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_arbitrary"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dlib"
|
name = "dlib"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
|
@ -335,8 +349,10 @@ dependencies = [
|
||||||
name = "fwd-fuzz"
|
name = "fwd-fuzz"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
"fwd",
|
"fwd",
|
||||||
"libfuzzer-sys",
|
"libfuzzer-sys",
|
||||||
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -433,6 +449,12 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobserver"
|
name = "jobserver"
|
||||||
version = "0.1.32"
|
version = "0.1.32"
|
||||||
|
|
@ -817,6 +839,12 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
@ -849,6 +877,18 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.124"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook"
|
name = "signal-hook"
|
||||||
version = "0.3.17"
|
version = "0.3.17"
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ edition = "2021"
|
||||||
cargo-fuzz = true
|
cargo-fuzz = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
arbitrary = { version = "1.3.2", features = ["derive"] }
|
||||||
libfuzzer-sys = "0.4"
|
libfuzzer-sys = "0.4"
|
||||||
|
serde_json = "1.0.124"
|
||||||
|
|
||||||
[dependencies.fwd]
|
[dependencies.fwd]
|
||||||
path = ".."
|
path = ".."
|
||||||
|
|
@ -19,3 +21,10 @@ path = "fuzz_targets/json_raw_input.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
doc = false
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "json_only_valid_serde"
|
||||||
|
path = "fuzz_targets/json_only_valid_serde.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
bench = false
|
||||||
|
|
|
||||||
77
fuzz/fuzz_targets/json_only_valid_serde.rs
Normal file
77
fuzz/fuzz_targets/json_only_valid_serde.rs
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use arbitrary::{Arbitrary, Error, Unstructured};
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
extern crate fwd;
|
||||||
|
use fwd::server::refresh::docker::JsonValue;
|
||||||
|
|
||||||
|
/// InputNumber is a JSON number, i.e., a finite 64-bit floating point value
|
||||||
|
/// that is not NaN. We need to define our own little wrapper here so that we
|
||||||
|
/// can convince Arbitrary to only make finite f64s.
|
||||||
|
///
|
||||||
|
/// Ideally we would actually wrap serde_json::Number but there are rules
|
||||||
|
/// about mixing 3rd party traits with 3rd party types.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
struct InputNumber(f64);
|
||||||
|
|
||||||
|
impl<'a> Arbitrary<'a> for InputNumber {
|
||||||
|
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self, Error> {
|
||||||
|
let value = f64::arbitrary(u)?;
|
||||||
|
if value.is_finite() {
|
||||||
|
Ok(InputNumber(value))
|
||||||
|
} else {
|
||||||
|
Err(Error::IncorrectFormat) // REJECT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn size_hint(depth: usize) -> (usize, Option<usize>) {
|
||||||
|
f64::size_hint(depth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TestInput is basically serde_json::Value, except (a) it has a HashMap and
|
||||||
|
/// not serde_json's special `Map` structure, and (b) it has `InputNumber`
|
||||||
|
/// instead of `json_serde::Number` for reasons described above.
|
||||||
|
#[derive(Debug, PartialEq, Arbitrary)]
|
||||||
|
enum TestInput {
|
||||||
|
Null,
|
||||||
|
Bool(bool),
|
||||||
|
Number(InputNumber),
|
||||||
|
String(String),
|
||||||
|
Object(HashMap<String, TestInput>),
|
||||||
|
Array(Vec<TestInput>),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert(value: &TestInput) -> serde_json::Value {
|
||||||
|
match value {
|
||||||
|
TestInput::Null => serde_json::Value::Null,
|
||||||
|
TestInput::Bool(b) => serde_json::Value::Bool(*b),
|
||||||
|
TestInput::Number(n) => serde_json::Value::Number(
|
||||||
|
serde_json::Number::from_f64(n.0).expect("Unable to make an f64"),
|
||||||
|
),
|
||||||
|
TestInput::String(s) => serde_json::Value::String(s.clone()),
|
||||||
|
TestInput::Object(o) => {
|
||||||
|
let mut out = serde_json::map::Map::new();
|
||||||
|
for (k, v) in o.into_iter() {
|
||||||
|
out.insert(k.clone(), convert(v));
|
||||||
|
}
|
||||||
|
serde_json::Value::Object(out)
|
||||||
|
}
|
||||||
|
TestInput::Array(v) => {
|
||||||
|
serde_json::Value::Array(v.into_iter().map(convert).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fuzz_target!(|data: TestInput| {
|
||||||
|
// Convert the arbitrary TestInput into an arbitrary serde_json::Value,
|
||||||
|
// then use serde_json to write out arbitrary JSON.
|
||||||
|
let converted = convert(&data).to_string();
|
||||||
|
|
||||||
|
// Parse the JSON that serde_json produced. This fuzz test should ensure
|
||||||
|
// that we can parse anything that serde_json can produce.
|
||||||
|
let _ = JsonValue::parse(converted.as_bytes());
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue