summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml12
-rw-r--r--.gitignore4
-rw-r--r--Cargo.lock38
-rw-r--r--Cargo.toml3
-rw-r--r--tests/fuzz/Cargo.toml27
-rw-r--r--tests/fuzz/src/compile.rs74
-rw-r--r--tests/fuzz/src/parse.rs8
7 files changed, 165 insertions, 1 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 366ceab7..c691869e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -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
diff --git a/.gitignore b/.gitignore
index 9a368e5e..e3ba6a57 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/Cargo.lock b/Cargo.lock
index b5a869b6..a9a3fde8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -103,6 +103,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -220,6 +226,7 @@ version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
+ "jobserver",
"libc",
]
@@ -1179,6 +1186,15 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1253,6 +1269,17 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2731,6 +2758,17 @@ dependencies = [
]
[[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"
dependencies = [
diff --git a/Cargo.toml b/Cargo.toml
index b8359f7d..1ddd504f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/tests/fuzz/Cargo.toml b/tests/fuzz/Cargo.toml
new file mode 100644
index 00000000..1b9e6b26
--- /dev/null
+++ b/tests/fuzz/Cargo.toml
@@ -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
diff --git a/tests/fuzz/src/compile.rs b/tests/fuzz/src/compile.rs
new file mode 100644
index 00000000..deb71778
--- /dev/null
+++ b/tests/fuzz/src/compile.rs
@@ -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);
+});
diff --git a/tests/fuzz/src/parse.rs b/tests/fuzz/src/parse.rs
new file mode 100644
index 00000000..f151661a
--- /dev/null
+++ b/tests/fuzz/src/parse.rs
@@ -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));
+});