Add simple fuzz testing (#2581)
This commit is contained in:
parent
0ebce56b36
commit
9aadb18186
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@ -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
4
.gitignore
vendored
@ -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
38
Cargo.lock
generated
@ -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"
|
||||
|
@ -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
27
tests/fuzz/Cargo.toml
Normal 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
74
tests/fuzz/src/compile.rs
Normal 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
8
tests/fuzz/src/parse.rs
Normal 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));
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user