WAI is a container + capability-dispatch standard for media. It does not re-implement codecs; it dispatches to SOTA standard libraries (PNG, FLAC, zstd as the mandatory floor; AVIF, JPEG-XL, Opus, AV1, XZ as the recommended modern set) and adds an envelope that lets a neural shared-prior path coexist with the model-free floor. The capability a file requires is named, not supplied. Two paths open every conforming file: the neural condition (sink regenerates from a shared ambient prior) and the zeroth condition (registered SOTA codec menu). Apache-2.0. Adds: - wai/SPEC.md — WAI v1.0 spec (envelope, manifest, capability dispatch, registered capabilities including v1.1 multi-rendition extension) - wai/wai-rs/ — Rust reference implementation: lib + cdylib + staticlib, C FFI, 11 capability + envelope round-trip tests passing - wai/wai-web/ — wasm-bindgen wrapper for browser sinks - wai/wai-neural/ — Python tooling for neural-condition models - wai/tools/ — codec export + encode helpers (Python) - wai/corpus/CORPUS.md — third-party test corpus reacquisition guide Updates README.md to list four standards instead of three. Known consumer: CommunicationOS uses WAI for media transport over MoQT/MLS sessions. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
87 lines
3.2 KiB
Rust
87 lines
3.2 KiB
Rust
//! Lightweight fuzz-smoke for every decoder. Feeds adversarial random
|
|
//! byte sequences to each codec's decode entry point and asserts that
|
|
//! none of them panic, segfault, or hang. This catches the bug class
|
|
//! that the corpus bench can't (the corpus is all VALID input by
|
|
//! construction; fuzz hits the malformed-input path the wrappers'
|
|
//! `catch_unwind`s and bounds-checks are supposed to handle).
|
|
//!
|
|
//! Stays on stable Rust (no libFuzzer/AFL needed). For deeper coverage
|
|
//! drop in `cargo-fuzz` targets later — this catches the cheap ones.
|
|
|
|
use std::panic;
|
|
|
|
const N: usize = 1024; // tries per codec; bumps if a target needs more
|
|
const MAX_LEN: usize = 16_384; // ceiling — large enough for header chases
|
|
|
|
fn lcg(state: &mut u64) -> u8 {
|
|
// xorshift64* — deterministic, dependency-free; not cryptographic.
|
|
*state ^= *state >> 12;
|
|
*state ^= *state << 25;
|
|
*state ^= *state >> 27;
|
|
(state.wrapping_mul(0x2545F4914F6CDD1D) >> 32) as u8
|
|
}
|
|
|
|
fn rand_bytes(seed: u64, len: usize) -> Vec<u8> {
|
|
let mut s = seed.max(1);
|
|
(0..len).map(|_| lcg(&mut s)).collect()
|
|
}
|
|
|
|
/// Run `n` adversarial inputs through `decode` and assert it never
|
|
/// panics. Failures of the codec on invalid input are expected — they
|
|
/// should return `Err`, NOT unwind.
|
|
fn fuzz<F>(name: &str, n: usize, decode: F)
|
|
where F: Fn(&[u8]) -> bool + std::panic::RefUnwindSafe
|
|
{
|
|
for i in 0..n {
|
|
let seed = (name.bytes().map(|b| b as u64).sum::<u64>() ^ i as u64).wrapping_mul(0x9E37);
|
|
// Vary length: empty, tiny, medium, large
|
|
let len = match i % 4 {
|
|
0 => 0,
|
|
1 => 1 + (i % 31),
|
|
2 => 64 + (i % 256),
|
|
_ => 1 + (seed as usize % MAX_LEN),
|
|
};
|
|
let buf = rand_bytes(seed, len);
|
|
let r = panic::catch_unwind(|| decode(&buf));
|
|
assert!(r.is_ok(),
|
|
"{} decoder panicked on adversarial input (seed={seed}, len={len})",
|
|
name);
|
|
}
|
|
}
|
|
|
|
// silence the "should never succeed" lint — decode result is intentionally ignored
|
|
fn ok<T, E>(_: Result<T, E>) -> bool { true }
|
|
|
|
#[test] fn fuzz_png_decode() {
|
|
fuzz("png", N, |b| ok(wai::codecs::image::png_decode(b)));
|
|
}
|
|
#[test] fn fuzz_jpeg_decode() {
|
|
fuzz("jpeg", N, |b| ok(wai::codecs::image::jpeg_decode(b)));
|
|
}
|
|
#[test] fn fuzz_avif_decode() {
|
|
fuzz("avif", N, |b| ok(wai::codecs::image::avif_decode(b)));
|
|
}
|
|
#[test] fn fuzz_jxl_decode() {
|
|
fuzz("jxl", N, |b| ok(wai::codecs::image::jxl_decode(b)));
|
|
}
|
|
#[test] fn fuzz_flac_decode() {
|
|
fuzz("flac", N, |b| ok(wai::codecs::audio::flac_decode(b)));
|
|
}
|
|
#[test] fn fuzz_opus_decode() {
|
|
fuzz("opus", N, |b| ok(wai::codecs::audio::opus_decode(b)));
|
|
}
|
|
#[test] fn fuzz_zstd_decode() {
|
|
fuzz("zstd", N, |b| ok(wai::codecs::text::zstd_decode(b)));
|
|
}
|
|
#[test] fn fuzz_xz_decode() {
|
|
fuzz("xz", N, |b| ok(wai::codecs::text::xz_decode(b)));
|
|
}
|
|
#[test] fn fuzz_av1_decode() {
|
|
// av1_decode has explicit bounds checks (the av1_decode_garbage
|
|
// robustness test caught the dav1d-abort path); confirm under random
|
|
// load too.
|
|
fuzz("av1", N, |b| ok(wai::codecs::video::av1_decode(b)));
|
|
}
|
|
#[test] fn fuzz_envelope_unpack() {
|
|
fuzz("envelope", N, |b| ok(wai::container::Wai::from_bytes(b)));
|
|
}
|