Some checks failed
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Has been cancelled
no-vanity-metrics / WAI public text names no comparison (pull_request) Has been cancelled
no-vanity-metrics / WAI public text names no company or product (pull_request) Has been cancelled
standards workspaces build + test / jouleclaw-rs (pull_request) Has been cancelled
standards workspaces build + test / aac-rs (pull_request) Has been cancelled
standards workspaces build + test / arl-rs (pull_request) Has been cancelled
standards workspaces build + test / eoc-rs (pull_request) Has been cancelled
standards workspaces build + test / jcp-rs (pull_request) Has been cancelled
standards workspaces build + test / joule-code-rs (pull_request) Has been cancelled
standards workspaces build + test / joule-ui-rs (pull_request) Has been cancelled
standards workspaces build + test / joulecontract-rs (pull_request) Has been cancelled
standards workspaces build + test / jouledesk-rs (pull_request) Has been cancelled
standards workspaces build + test / joulehook-rs (pull_request) Has been cancelled
standards workspaces build + test / map-rs (pull_request) Has been cancelled
standards workspaces build + test / mesh-rs (pull_request) Has been cancelled
standards workspaces build + test / openpay (pull_request) Has been cancelled
standards workspaces build + test / proof-rs (pull_request) Has been cancelled
standards workspaces build + test / sandbox-rs (pull_request) Has been cancelled
standards workspaces build + test / sense-rs (pull_request) Has been cancelled
standards workspaces build + test / smart-byte-rs (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · macos (pull_request) Has been cancelled
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Has been cancelled
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Has been cancelled
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · macos (pull_request) Has been cancelled
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Has been cancelled
WAI energy receipts / energy receipts · macos (pull_request) Has been cancelled
WAI section references / cited sections exist (pull_request) Has been cancelled
wai-vector vector graphics / linux-aarch64 (pull_request) Has been cancelled
wai-vector vector graphics / linux-x86_64 (pull_request) Has been cancelled
wai-vector vector graphics / macos-arm64 (pull_request) Has been cancelled
wai-vector vector graphics / wasm32-wasip2 (wasmtime) (pull_request) Has been cancelled
wai-vector vector graphics / wasm32 under Node (pull_request) Has been cancelled
wai-vector vector graphics / fuzz, peak allocation, comparison count (pull_request) Has been cancelled
workflow hygiene / every workflow supersedes its own queue (pull_request) Has been cancelled
The D7-2 cleanup (no generated byte changes), with the fuzz seed conflict resolved to D7-2's literal; the raster's chunked loops on as_chunks, collapsed conditionals, and no placeholder bindings. Co-Authored-By: Claude Opus 5.5 <[email protected]>
117 lines
4.6 KiB
Rust
117 lines
4.6 KiB
Rust
//! A deterministic, corpus-seeded fuzz of the `WVG1` and `WOS1` parsers (§2.7): no
|
|
//! input panics, every input accepted writes back to its own bytes, and an accepted
|
|
//! payload of small cost renders its first frame. Seeds: every vector of the corpus
|
|
//! and the files under `fuzz/seeds/`.
|
|
//!
|
|
//! `WAI_VECTOR_FUZZ_ITERS` sets the mutations per seed (default 40).
|
|
|
|
use std::panic::{AssertUnwindSafe, catch_unwind};
|
|
use std::path::PathBuf;
|
|
|
|
use wai_vector::vectors::{all_vectors, corpus_store};
|
|
use wai_vector::{wos1, wvg1};
|
|
|
|
struct Rng(u64);
|
|
|
|
impl Rng {
|
|
fn next(&mut self) -> u64 {
|
|
self.0 ^= self.0 << 13;
|
|
self.0 ^= self.0 >> 7;
|
|
self.0 ^= self.0 << 17;
|
|
self.0
|
|
}
|
|
fn below(&mut self, n: usize) -> usize {
|
|
(self.next() % n.max(1) as u64) as usize
|
|
}
|
|
}
|
|
|
|
const INTERESTING: [u32; 14] = [0, 1, 2, 3, 15, 16, 255, 0xFFFF, 0x1_0000, 0x7FFF_FFFF, 0x8000_0000, 0xFFFF_FFFF, 1 << 20, (1 << 22) + 1];
|
|
|
|
fn mutate(seed: &[u8], r: &mut Rng) -> Vec<u8> {
|
|
let mut b = seed.to_vec();
|
|
for _ in 0..1 + r.below(4) {
|
|
let n = b.len();
|
|
match r.below(7) {
|
|
0 if n > 0 => {
|
|
let i = r.below(n);
|
|
b[i] ^= 1 << r.below(8);
|
|
}
|
|
1 if n > 0 => {
|
|
let i = r.below(n);
|
|
b[i] = r.next() as u8;
|
|
}
|
|
2 if n > 0 => b.truncate(r.below(n)),
|
|
3 => b.push(r.next() as u8),
|
|
4 if n > 0 => {
|
|
let i = r.below(n);
|
|
b.insert(i, r.next() as u8);
|
|
}
|
|
5 if n >= 4 => {
|
|
// A field-sized value at a 4-aligned offset, most often in a header.
|
|
let i = if r.below(2) == 0 { 4 * r.below(20.min(n / 4)) } else { 4 * r.below(n / 4) };
|
|
let v = INTERESTING[r.below(INTERESTING.len())];
|
|
b[i..i + 4].copy_from_slice(&v.to_le_bytes());
|
|
}
|
|
6 if n >= 2 => {
|
|
let i = r.below(n - 1);
|
|
let v = INTERESTING[r.below(INTERESTING.len())] as u16;
|
|
b[i..i + 2].copy_from_slice(&v.to_le_bytes());
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
b
|
|
}
|
|
|
|
#[test]
|
|
fn fuzz_smoke() {
|
|
let iters: usize = std::env::var("WAI_VECTOR_FUZZ_ITERS").ok().and_then(|s| s.parse().ok()).unwrap_or(40);
|
|
let mut seeds: Vec<(bool, Vec<u8>)> = all_vectors()
|
|
.into_iter()
|
|
.filter(|v| v.ext != "json" && v.bytes.len() <= 1 << 16)
|
|
.map(|v| (v.ext == "wos1", v.bytes))
|
|
.collect();
|
|
let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fuzz/seeds");
|
|
let mut names: Vec<PathBuf> = std::fs::read_dir(&dir).expect("fuzz/seeds").flatten().map(|e| e.path()).collect();
|
|
names.sort();
|
|
assert!(!names.is_empty());
|
|
for p in names {
|
|
seeds.push((p.extension().is_some_and(|x| x == "wos1"), std::fs::read(&p).unwrap()));
|
|
}
|
|
let mut store = corpus_store();
|
|
let mut r = Rng(0x5EED_0FD7_0002);
|
|
let (mut runs, mut accepted, mut rendered) = (0usize, 0usize, 0usize);
|
|
for (is_wos1, seed) in &seeds {
|
|
for _ in 0..iters {
|
|
let input = mutate(seed, &mut r);
|
|
let ok = catch_unwind(AssertUnwindSafe(|| {
|
|
if *is_wos1 {
|
|
wos1::parse(&input).ok().map(|w| w.write())
|
|
} else {
|
|
wvg1::load(&input, &mut store).ok().map(|l| {
|
|
// An accepted payload renders, as a sink with a budget would: the
|
|
// first frame at the first size, when that size's cost is small.
|
|
let d = &l.doc;
|
|
let p = d.rates[0].ticks_per_frame(d.header.tb);
|
|
if let Some(g) = wai_vector::clock::frames(d.header.t0, d.header.dur, p).next()
|
|
&& l.costs[0] <= 1 << 22
|
|
{
|
|
let px = wai_vector::frame::render(&l, 0, 0, g).expect("a frame in range renders");
|
|
assert_eq!(px.len(), 4 * usize::from(d.sizes[0].w) * usize::from(d.sizes[0].h));
|
|
rendered += 1;
|
|
}
|
|
l.doc.write()
|
|
})
|
|
}
|
|
}));
|
|
let out = ok.unwrap_or_else(|_| panic!("a parser panicked on input {}", wai_vector::json::hex(&input)));
|
|
if let Some(written) = out {
|
|
assert_eq!(written, input, "an accepted input writes back to its own bytes");
|
|
accepted += 1;
|
|
}
|
|
runs += 1;
|
|
}
|
|
}
|
|
eprintln!("fuzz smoke: {runs} inputs from {} seeds, {accepted} accepted, {rendered} rendered", seeds.len());
|
|
assert!(runs >= seeds.len() * iters);
|
|
}
|