//! Replay every committed fuzz input through the oracle, on stable. //! //! The soak tier runs on astra, on nightly, when the box is idle. That is the //! wrong place for the only copy of a property to live: a crash found there is //! fixed here, and nothing on a developer's machine would notice it coming //! back. //! //! So both corpora replay as an ordinary `cargo test`: //! //! - `fuzz/seeds/apc/` is human intent and real client traffic, the shapes //! worth reaching first. //! - `fuzz/regressions/` is inputs that once found a bug. Anything landing //! there is a permanent test by virtue of the directory, with no test //! function to write and no chance of forgetting one. //! //! The assertion is `kittygfx::oracle::check_bodies`, the same function the //! libFuzzer target calls, so the two cannot check different things. use std::path::{Path, PathBuf}; fn dir(rel: &str) -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")).join(rel) } fn replay(dir: &Path) -> usize { let Ok(entries) = std::fs::read_dir(dir) else { return 0; }; let mut count = 0; for entry in entries { let path = entry.expect("readable dir entry").path(); if !path.is_file() { continue; } if path.extension().is_some_and(|e| e == "md") { continue; } let bytes = std::fs::read(&path).expect("readable input"); let outcome = std::panic::catch_unwind(|| kittygfx::oracle::check_bodies(&bytes)); assert!(outcome.is_ok(), "oracle failed on {}", path.display()); count += 1; } count } #[test] fn every_seed_holds_the_oracle() { let n = replay(&dir("fuzz/seeds/apc")); // A seed directory that silently emptied would make this test pass while // checking nothing, which is the failure mode a corpus-replay test has. assert!( n >= 30, "expected the committed seed corpus, found {n} inputs" ); } #[test] fn every_regression_holds_the_oracle() { // No lower bound. This is empty until the first crash, and that is the // good state. replay(&dir("fuzz/regressions")); } /// The oracle must actually run, not merely not panic. #[test] fn the_oracle_feeds_the_bodies_it_is_given() { let input = b"\x1b_Ga=q,f=24,t=d,i=3;AAAA\x1b\\\x1b_Ga=d,d=A\x1b\\"; assert_eq!(kittygfx::oracle::check_bodies(input), 2); } /// The two ways a partial transmission can grow without bound, replayed at /// sizes the committed corpus cannot carry. /// /// `fuzz/regressions/` holds a small witness for each. Neither growth /// amplifies: the id-space one is cheap per entry and the single-id one runs /// under 1:1, so what fails on a parser that evicts nothing is feeding it more /// than the caps, and that is a hundred megabytes, not a file to commit. #[test] fn unbounded_transmission_growth_stays_capped() { // 200,000 unfinished chunks under distinct ids. Without eviction each one // leaves an entry behind for the life of the terminal. let mut many = Vec::new(); for i in 0..200_000u32 { many.extend_from_slice(format!("\x1b_Ga=T,f=32,i={i},m=1;QUJD\x1b\\").as_bytes()); } kittygfx::oracle::check_bodies(&many); // A single id fed past the budget, never finished. Without the cap, // retained memory is bounded only by how many bytes the writer sends. let chunk = "A".repeat(4096); // Two and a half times the budget. Enough that an uncapped parser lands // over the absolute ceiling rather than merely near it; a capped one stops // decoding at the budget, so the extra costs it nothing. let bodies = (kittygfx::MAX_IN_FLIGHT_BYTES / 3072) * 5 / 2; let mut one = Vec::new(); for _ in 0..bodies { one.extend_from_slice(format!("\x1b_Ga=T,f=32,i=1,m=1;{chunk}\x1b\\").as_bytes()); } kittygfx::oracle::check_bodies(&one); }