Add simple fuzz testing (#2581)

This commit is contained in:
Nathaniel Brough 2023-12-05 10:26:58 -08:00 committed by GitHub
parent 0ebce56b36
commit 9aadb18186
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 165 additions and 1 deletions

View File

@ -35,3 +35,15 @@ jobs:
- uses: dtolnay/rust-toolchain@1.70.0
- uses: Swatinem/rust-cache@v2
- run: cargo check --workspace
fuzz:
name: Check fuzzers
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly-2023-09-13
- uses: Swatinem/rust-cache@v2
- run: cargo install cargo-fuzz
- run: cd tests/fuzz && cargo fuzz build --dev

4
.gitignore vendored
View File

@ -10,6 +10,10 @@ tests/png
tests/pdf
tests/svg
tests/target
tests/fuzz/target
tests/fuzz/corpus
tests/fuzz/artifacts
tests/fuzz/coverage
tarpaulin-report.html
# Rust

38
Cargo.lock generated
View File

@ -102,6 +102,12 @@ dependencies = [
"num-traits",
]
[[package]]
name = "arbitrary"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
[[package]]
name = "arrayref"
version = "0.3.7"
@ -220,6 +226,7 @@ version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"jobserver",
"libc",
]
@ -1178,6 +1185,15 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "jobserver"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
dependencies = [
"libc",
]
[[package]]
name = "jpeg-decoder"
version = "0.3.0"
@ -1252,6 +1268,17 @@ dependencies = [
"libdeflate-sys",
]
[[package]]
name = "libfuzzer-sys"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
dependencies = [
"arbitrary",
"cc",
"once_cell",
]
[[package]]
name = "libm"
version = "0.2.8"
@ -2730,6 +2757,17 @@ dependencies = [
"yaml-front-matter",
]
[[package]]
name = "typst-fuzz"
version = "0.10.0"
dependencies = [
"comemo",
"libfuzzer-sys",
"typst",
"typst-render",
"typst-syntax",
]
[[package]]
name = "typst-ide"
version = "0.10.0"

View File

@ -1,5 +1,5 @@
[workspace]
members = ["crates/*", "tests"]
members = ["crates/*", "tests", "tests/fuzz"]
default-members = ["crates/typst-cli"]
resolver = "2"
@ -58,6 +58,7 @@ include_dir = "0.7"
indexmap = { version = "2", features = ["serde"] }
inferno = "0.11.15"
kurbo = "0.9"
libfuzzer-sys = "0.4"
lipsum = "0.9"
log = "0.4"
miniz_oxide = "0.7"

27
tests/fuzz/Cargo.toml Normal file
View File

@ -0,0 +1,27 @@
[package]
name = "typst-fuzz"
version.workspace = true
edition.workspace = true
publish = false
[package.metadata]
cargo-fuzz = true
[dependencies]
typst = { workspace = true }
typst-render = { workspace = true }
typst-syntax = { workspace = true }
comemo = { workspace = true }
libfuzzer-sys = { workspace = true }
[[bin]]
name = "parse"
path = "src/parse.rs"
test = false
doc = false
[[bin]]
name = "compile"
path = "src/compile.rs"
test = false
doc = false

74
tests/fuzz/src/compile.rs Normal file
View File

@ -0,0 +1,74 @@
#![no_main]
use comemo::Prehashed;
use libfuzzer_sys::fuzz_target;
use typst::diag::{FileError, FileResult};
use typst::eval::Tracer;
use typst::foundations::{Bytes, Datetime};
use typst::syntax::{FileId, Source};
use typst::text::{Font, FontBook};
use typst::visualize::Color;
use typst::{Library, World};
const FONT: &[u8] = include_bytes!("../../../assets/fonts/LinLibertine_R.ttf");
struct FuzzWorld {
library: Prehashed<Library>,
book: Prehashed<FontBook>,
font: Font,
source: Source,
}
impl FuzzWorld {
fn new(text: &str) -> Self {
let font = Font::new(FONT.into(), 0).unwrap();
let book = FontBook::from_fonts([&font]);
Self {
library: Prehashed::new(Library::build()),
book: Prehashed::new(book),
font,
source: Source::detached(text),
}
}
}
impl World for FuzzWorld {
fn library(&self) -> &Prehashed<Library> {
&self.library
}
fn book(&self) -> &Prehashed<FontBook> {
&self.book
}
fn main(&self) -> Source {
self.source.clone()
}
fn source(&self, src: FileId) -> FileResult<Source> {
Err(FileError::NotFound(src.vpath().as_rootless_path().into()))
}
fn file(&self, src: FileId) -> FileResult<Bytes> {
Err(FileError::NotFound(src.vpath().as_rootless_path().into()))
}
fn font(&self, _: usize) -> Option<Font> {
Some(self.font.clone())
}
fn today(&self, _: Option<i64>) -> Option<Datetime> {
None
}
}
fuzz_target!(|text: &str| {
let world = FuzzWorld::new(text);
let mut tracer = Tracer::new();
if let Ok(document) = typst::compile(&world, &mut tracer) {
if let Some(page) = document.pages.first() {
std::hint::black_box(typst_render::render(page, 1.0, Color::WHITE));
}
}
comemo::evict(10);
});

8
tests/fuzz/src/parse.rs Normal file
View File

@ -0,0 +1,8 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use typst_syntax::parse;
fuzz_target!(|text: &str| {
std::hint::black_box(parse(text));
});