From fab5db9511ca020ca43bc8b36525fc274e4e6b66 Mon Sep 17 00:00:00 2001 From: Tpt Date: Sat, 4 Mar 2023 18:14:03 +0100 Subject: [PATCH] Fuzzer: SPARQL results: Makes the fuzzers test serialization too --- fuzz/Cargo.toml | 5 ++ fuzz/fuzz_targets/sparql_results_json.rs | 15 ++---- fuzz/fuzz_targets/sparql_results_tsv.rs | 10 ++-- fuzz/fuzz_targets/sparql_results_xml.rs | 10 ++-- fuzz/src/lib.rs | 1 + fuzz/src/result_format.rs | 63 ++++++++++++++++++++++++ 6 files changed, 78 insertions(+), 26 deletions(-) create mode 100644 fuzz/src/lib.rs create mode 100644 fuzz/src/result_format.rs diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 7b812526..0c8a0b72 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" cargo-fuzz = true [dependencies] +anyhow = "1" lazy_static = "1" libfuzzer-sys = "0.4" spargebra = { path = "../lib/spargebra", features = ["rdf-star", "sep-0006"] } @@ -16,6 +17,10 @@ sparesults = { path = "../lib/sparesults", features = ["rdf-star"] } sparql-smith = { path = "../lib/sparql-smith", features = ["sep-0006"] } oxigraph = { path = "../lib" } +[profile.release] +codegen-units = 1 +debug = true + [workspace] [[bin]] diff --git a/fuzz/fuzz_targets/sparql_results_json.rs b/fuzz/fuzz_targets/sparql_results_json.rs index c5b4ddcb..cd917481 100644 --- a/fuzz/fuzz_targets/sparql_results_json.rs +++ b/fuzz/fuzz_targets/sparql_results_json.rs @@ -1,15 +1,6 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use sparesults::{QueryResultsFormat, QueryResultsParser, QueryResultsReader}; +use oxigraph_fuzz::result_format::fuzz_result_format; +use sparesults::QueryResultsFormat; -fuzz_target!(|data: &[u8]| { - let parser = QueryResultsParser::from_format(QueryResultsFormat::Json); - if let Ok(QueryResultsReader::Solutions(solutions)) = parser.read_results(data) { - for s in solutions { - if s.is_err() { - // TODO: avoid infinite loop of errors - break; - } - } - } -}); +fuzz_target!(|data: &[u8]| { fuzz_result_format(QueryResultsFormat::Json, data) }); diff --git a/fuzz/fuzz_targets/sparql_results_tsv.rs b/fuzz/fuzz_targets/sparql_results_tsv.rs index ec69ab94..4cf3f4cf 100644 --- a/fuzz/fuzz_targets/sparql_results_tsv.rs +++ b/fuzz/fuzz_targets/sparql_results_tsv.rs @@ -1,10 +1,6 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use sparesults::{QueryResultsFormat, QueryResultsParser, QueryResultsReader}; +use oxigraph_fuzz::result_format::fuzz_result_format; +use sparesults::QueryResultsFormat; -fuzz_target!(|data: &[u8]| { - let parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv); - if let Ok(QueryResultsReader::Solutions(solutions)) = parser.read_results(data) { - for _ in solutions {} - } -}); +fuzz_target!(|data: &[u8]| { fuzz_result_format(QueryResultsFormat::Tsv, data) }); diff --git a/fuzz/fuzz_targets/sparql_results_xml.rs b/fuzz/fuzz_targets/sparql_results_xml.rs index 57c0c4cb..6c4747ec 100644 --- a/fuzz/fuzz_targets/sparql_results_xml.rs +++ b/fuzz/fuzz_targets/sparql_results_xml.rs @@ -1,10 +1,6 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use sparesults::{QueryResultsFormat, QueryResultsParser, QueryResultsReader}; +use oxigraph_fuzz::result_format::fuzz_result_format; +use sparesults::QueryResultsFormat; -fuzz_target!(|data: &[u8]| { - let parser = QueryResultsParser::from_format(QueryResultsFormat::Xml); - if let Ok(QueryResultsReader::Solutions(solutions)) = parser.read_results(data) { - for _ in solutions {} - } -}); +fuzz_target!(|data: &[u8]| { fuzz_result_format(QueryResultsFormat::Xml, data) }); diff --git a/fuzz/src/lib.rs b/fuzz/src/lib.rs new file mode 100644 index 00000000..33c1a891 --- /dev/null +++ b/fuzz/src/lib.rs @@ -0,0 +1 @@ +pub mod result_format; diff --git a/fuzz/src/result_format.rs b/fuzz/src/result_format.rs new file mode 100644 index 00000000..cc00f9be --- /dev/null +++ b/fuzz/src/result_format.rs @@ -0,0 +1,63 @@ +use anyhow::Context; +use sparesults::{ + QueryResultsFormat, QueryResultsParser, QueryResultsReader, QueryResultsSerializer, +}; + +pub fn fuzz_result_format(format: QueryResultsFormat, data: &[u8]) { + let parser = QueryResultsParser::from_format(format); + let serializer = QueryResultsSerializer::from_format(format); + + let Ok(reader) = parser.read_results(data) else { + return; + }; + match reader { + QueryResultsReader::Solutions(solutions) => { + let Ok(solutions) = solutions.collect::, _>>() else { + return; + }; + + // We try to write again + let mut writer = serializer + .solutions_writer( + Vec::new(), + solutions + .get(0) + .map_or_else(Vec::new, |s| s.variables().to_vec()), + ) + .unwrap(); + for solution in &solutions { + writer.write(solution).unwrap(); + } + let serialized = String::from_utf8(writer.finish().unwrap()).unwrap(); + + // And to parse again + if let QueryResultsReader::Solutions(roundtrip_solutions) = parser + .read_results(serialized.as_bytes()) + .with_context(|| format!("Parsing {:?}", &serialized)) + .unwrap() + { + assert_eq!( + roundtrip_solutions + .collect::, _>>() + .with_context(|| format!("Parsing {:?}", &serialized)) + .unwrap(), + solutions + ) + } + } + QueryResultsReader::Boolean(value) => { + // We try to write again + let mut serialized = Vec::new(); + serializer + .write_boolean_result(&mut serialized, value) + .unwrap(); + + // And to parse again + if let QueryResultsReader::Boolean(roundtrip_value) = + parser.read_results(serialized.as_slice()).unwrap() + { + assert_eq!(roundtrip_value, value) + } + } + } +}