open-standards/wai/wai-rs/tests/int_payload_preflight.rs
Transaction Science dcae455cec
Some checks failed
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 3s
no-vanity-metrics / WAI public text names no comparison (pull_request) Successful in 4s
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 3s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m50s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m20s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m14s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m12s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m45s
wai cross-architecture byte-equality / linux-x86_64 (pull_request) Successful in 27s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 3m59s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 15s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 2m14s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 32s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 44s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 1m2s
wai formal verification (integer core) / kani (pull_request) Successful in 4m10s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 1m0s
WAI neural decoders end-to-end / synthetic fixtures → Rust decode → reference (±4) (pull_request) Successful in 1m16s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m9s
WAI section references / cited sections exist (pull_request) Successful in 11s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 3m51s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 1m46s
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 6m30s
standards workspaces build + test / aac-rs (pull_request) Successful in 2m28s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 2m1s
standards workspaces build + test / arl-rs (pull_request) Has been cancelled
standards workspaces build + test / joule-ui-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 / sense-rs (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backend (jcp-energy) (pull_request) Has been cancelled
wai cross-architecture byte-equality / linux-aarch64 (pull_request) Has been cancelled
wai cross-architecture byte-equality / macos-arm64 (reference) (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 / cross-arch byte-equality · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · macos (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
standards workspaces build + test / joulecontract-rs (pull_request) Has been cancelled
wai: the one-frame preflight clip has the canonical header
A one-frame WIV1 carries block 1 and mv_precision 1 (integer-payloads §3).
The starved one-frame clip in tests/int_payload_preflight.rs had block
2^32 - 1, so it was refused as field_range at parse, before the decode it
exists to measure. With block 1 it parses and is refused at its first
exhausted symbol, as the test asserts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-01 21:23:20 -04:00

243 lines
13 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! An integer payload whose header asks for more than the caps allow, or more than its
//! parameter set can fit, is refused before the decode allocates anything sized from
//! it (`extensions/integer-payloads.md` §7).
//!
//! Each case below is a committed payload with header fields changed. The parsers cap
//! every tensor a header sizes (`too_large`) before reading a section, and the decode
//! derives each network's shapes from its layer geometry before it allocates a latent;
//! this file checks both directly with a global allocator that records the largest
//! single allocation, and asserts that refusing each payload allocates at once no more
//! than parsing its parameter set needs (each parsed weight is held as an `i64`, so at
//! most four times the set's bytes) or a few kilobytes, whichever is more: nothing
//! sized from the header.
//!
//! Its own test binary with a single test, so that no other test's allocations land in
//! the window it measures.
#![cfg(all(feature = "video_int", feature = "audio_int", feature = "splat_int"))]
use std::alloc::{GlobalAlloc, Layout, System};
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
use wai::int_payload::{decode_int, resolve, IntParams};
use wai::pin::HeldBytes;
/// The system allocator, recording the largest single request.
struct Largest;
static LARGEST: AtomicUsize = AtomicUsize::new(0);
unsafe impl GlobalAlloc for Largest {
unsafe fn alloc(&self, l: Layout) -> *mut u8 {
LARGEST.fetch_max(l.size(), Relaxed);
unsafe { System.alloc(l) }
}
unsafe fn alloc_zeroed(&self, l: Layout) -> *mut u8 {
LARGEST.fetch_max(l.size(), Relaxed);
unsafe { System.alloc_zeroed(l) }
}
unsafe fn realloc(&self, p: *mut u8, l: Layout, new: usize) -> *mut u8 {
LARGEST.fetch_max(new, Relaxed);
unsafe { System.realloc(p, l, new) }
}
unsafe fn dealloc(&self, p: *mut u8, l: Layout) {
unsafe { System.dealloc(p, l) }
}
}
#[global_allocator]
static ALLOC: Largest = Largest;
/// The largest single allocation `f` makes, and its result.
fn largest_in<R>(f: impl FnOnce() -> R) -> (R, usize) {
LARGEST.store(0, Relaxed);
let r = f();
(r, LARGEST.load(Relaxed))
}
fn read(rel: &str) -> Vec<u8> {
let p = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/vectors").join(rel);
std::fs::read(&p).unwrap_or_else(|e| panic!("{}: {e}", p.display()))
}
/// `p` with the u32 fields at the given offsets replaced.
fn with(p: &[u8], fields: &[(usize, u32)]) -> Vec<u8> {
let mut p = p.to_vec();
for &(off, v) in fields {
p[off..off + 4].copy_from_slice(&v.to_le_bytes());
}
p
}
const FEW_KB: usize = 16 << 10;
/// A rANS stream of `n` symbols, symbol `i` coded with `(start, freq)` from `sym(i)`,
/// from the state 2^31, so it ends canonically: what an encoder writes.
fn encode(n: u64, sym: impl Fn(u64) -> (u64, u64)) -> Vec<u8> {
const L: u64 = 1 << 31;
let mut state = L;
let mut renorm: Vec<u32> = Vec::new();
let mut i = n;
while i > 0 {
i -= 1;
let (start, freq) = sym(i);
let x_max = ((L >> 16) << 32) * freq;
while state >= x_max {
renorm.push(state as u32);
state >>= 32;
}
state = ((state / freq) << 16) + (state % freq) + start;
}
renorm.reverse();
let mut words = vec![state as u32, (state >> 32) as u32];
words.extend(renorm);
words.iter().flat_map(|w| w.to_le_bytes()).collect()
}
#[test]
fn a_header_past_the_caps_or_the_model_is_refused_before_anything_is_allocated() {
use wai::codecs as c;
let hyp: BTreeMap<String, Vec<u8>> = [("entropy.json", "int_hyper/tiny_entropy.json"), ("g_s.whm", "int_hyper/tiny_g_s.bin"), ("h_s.whm", "int_hyper/tiny_h_s.bin")]
.into_iter().map(|(k, v)| (k.to_string(), read(v))).collect();
let hyp_view: BTreeMap<String, &[u8]> = hyp.iter().map(|(k, v)| (k.clone(), v.as_slice())).collect();
let unpinned = |cap, held| resolve(cap, None, &[held], None).expect("an unpinned set resolves");
let hyp = unpinned(c::CAP_NEURAL_INT_HYPER, HeldBytes::Set { listing: b"", files: &hyp_view });
let (model, gs) = (read("int_audio/audio_model.bin"), read("int_splat/splat_gs.bin"));
let none = IntParams::none();
let (aud, spl) = (unpinned(c::CAP_AUDIO_INT_CODEC, HeldBytes::File(&model)), unpinned(c::CAP_SPLAT_INT_CODEC, HeldBytes::File(&gs)));
let (iv, nk, ih) = (read("int_video/int_video_bitstream.wiv1"), read("int_video/video_nk_bitstream.wiv1"), read("int_hyper/tiny_bitstream.wih1"));
let (au, sp) = (read("int_audio/audio_bitstream.wia1"), read("int_splat/splat.wis1"));
// The counter sees a decode's allocations: the committed clip's largest is at least
// one 48 × 48 frame of RGB8.
let (r, largest) = largest_in(|| decode_int(c::CAP_VIDEO_INT_MOTION, &iv, &IntParams::none()));
r.expect("the committed clip decodes");
assert!(largest >= 48 * 48 * 3, "the counter saw a {largest}-byte allocation at most");
let hyp_bytes: usize = hyp_view.values().map(|v| v.len()).sum();
let cases: Vec<(&str, &str, Vec<u8>, &IntParams<'_>, &str, usize)> = vec![
// A 2^20-frame clip of 48 × 48: each frame is within the cap, the clip is not.
("a 2^20-frame clip", c::CAP_VIDEO_INT_MOTION, with(&iv, &[(8, 1 << 20)]), &none, "too_large", 0),
// An 8192 × 8192 frame.
("an 8192 × 8192 frame", c::CAP_VIDEO_INT_HYPER, with(&nk, &[(12, 8192), (16, 8192)]), &hyp, "too_large", hyp_bytes),
// A hyper-latent of 2 × 6000 × 6000 = 72 million elements, past the parser's
// 2^26 cap.
("a 72-million-element hyper-latent", c::CAP_NEURAL_INT_HYPER, with(&ih, &[(24, 6000), (28, 6000)]), &hyp, "too_large", hyp_bytes),
// A hyper-latent of 2 × 2000 × 2000, within the cap, that h_s would expand to
// 8 × 4000 × 4000, past it.
("a hyper-latent h_s expands past the cap", c::CAP_NEURAL_INT_HYPER, with(&ih, &[(24, 2000), (28, 2000)]), &hyp, "too_large", hyp_bytes),
// An audio latent of 16 × 2^20, within the cap, that the synthesis would
// upsample past it.
("an audio latent the synthesis expands past the cap", c::CAP_AUDIO_INT_CODEC, with(&au, &[(16, 1 << 20)]), &aud, "too_large", model.len()),
// A splat latent of 32 × 1024 × 1024, within the cap, that g_s would expand to
// 14 × 4096 × 4096, past it.
("a splat latent g_s expands past the cap", c::CAP_SPLAT_INT_CODEC, with(&sp, &[(12, 1024), (16, 1024)]), &spl, "too_large", gs.len()),
];
let parsed = wai::int_payload::wire::parse_wih1(&with(&ih, &[(24, 6000), (28, 6000)])).map(|_| ()).map_err(|e| e.code());
assert_eq!(parsed, Err("too_large"), "the parser's cap refuses the 72-million-element hyper-latent");
for (what, cap, bad, params, code, set_bytes) in cases {
let (r, largest) = largest_in(|| decode_int(cap, &bad, params));
let bound = FEW_KB.max(4 * set_bytes);
assert!(largest <= bound, "{what}: refusing it allocated {largest} bytes at once, over {bound} ({r:?})");
assert_eq!(r.map(|_| ()).map_err(|e| e.code()), Err(code), "{what}");
}
// Headers every cap admits, whose streams are 8 bytes and cannot carry what the
// header declares (§2, §7): each is refused at the first symbol that runs its
// stream out, having allocated only what it decoded. Before this, the 16-frame
// clip of 4729 × 4729 ran for about a minute and reached gigabytes before it was
// refused at its last symbol.
use wai::int_payload::wire;
let (zeros, ones) = ([0u8; 8], [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]);
let clip = |n_frames: u32, h: u32, w: u32, block: u32, cdf: Vec<i32>, rans: &[u8]| wire::write_wiv1(&wire::Wiv1 {
mv_precision: 1, keyframe: 0, n_frames, h, w, block, q: 1, offset: 0, cdf,
mvs: vec![(0, 0); n_frames as usize - 1], rans, wih1: None,
});
let wih1 = {
// The tiny h_s doubles the hyper-latent's sides.
let mut k = wire::parse_wih1(&ih).unwrap();
(k.zh, k.zw, k.sh, k.sw) = (64, 64, 128, 128);
k.z_rans = &ones[..];
wire::write_wih1(&k)
};
let wia1 = {
// The committed synthesis gives 16 samples per latent sample (512 → 8192), so
// t_out is what a latent of 4096 presents.
let mut a = wire::parse_wia1(&au).unwrap();
a.lat_len = 1 << 12;
a.t_out = 16 << 12;
a.rans = &ones[..];
wire::write_wia1(&a)
};
let wis1 = {
// The splat g_s quadruples the latent's sides.
let mut v = wire::parse_wis1(&sp).unwrap();
(v.sh, v.sw, v.s) = (64, 64, 256);
v.rans = &ones[..];
wire::write_wis1(&v)
};
let starved: Vec<(&str, &str, Vec<u8>, &IntParams<'_>, usize)> = vec![
// Small enough that a decode which ran on past the first exhausted symbol would
// finish here quickly, and allocate a 256 × 256 frame.
("16 frames of 256 × 256", c::CAP_VIDEO_INT_MOTION, clip(16, 256, 256, 1 << 31, vec![0, 1, 65_536], &zeros), &none, 0),
("16 frames of 4729 × 4729", c::CAP_VIDEO_INT_MOTION, clip(16, 4729, 4729, 1 << 31, vec![0, 1, 65_536], &zeros), &none, 0),
("one frame of 4096 × 5461", c::CAP_VIDEO_INT_MOTION, clip(1, 4096, 5461, 1, vec![0, 65_534, 65_535, 65_536], &ones), &none, 0),
("a hyper-latent of 2 × 64 × 64", c::CAP_NEURAL_INT_HYPER, wih1, &hyp, hyp_bytes),
("an audio latent of 16 × 4096", c::CAP_AUDIO_INT_CODEC, wia1, &aud, model.len()),
("a splat latent of 32 × 64 × 64", c::CAP_SPLAT_INT_CODEC, wis1, &spl, gs.len()),
];
for (what, cap, bad, params, set_bytes) in starved {
assert!(wai::container::check_int_payload_framing(cap, &bad).is_ok(), "{what}: the payload parses");
let (r, largest) = largest_in(|| decode_int(cap, &bad, params));
let bound = FEW_KB.max(4 * set_bytes);
assert!(largest <= bound, "{what}: refusing it allocated {largest} bytes at once, over {bound} ({r:?})");
assert_eq!(r.map(|_| ()).map_err(|e| e.code()), Err("stream_exhausted"), "{what}");
}
// A WIA1 of 384 bytes whose stream is fully valid (8.4 million symbols of frequency
// 65535/65536) for a latent of 16 × 524287 that presents one sample: its synthesis
// would hold gigabytes for that one sample. The latent is longer than t_out needs,
// so the plan refuses it (geometry_mismatch) before a symbol is decoded, and the
// declared cost refuses it alike.
let wia1 = {
let mut a = wire::parse_wia1(&au).unwrap();
let n = a.n_ch as usize;
a.lat_len = 524_287;
a.t_out = 1;
a.offset = 0;
a.median_q = vec![0; n];
a.cdfs = vec![vec![0, 65_535, 65_536]; n];
let rans = encode(n as u64 * 524_287, |_| (0, 65_535));
let mut b = a.clone();
b.rans = &rans;
wire::write_wia1(&b)
};
assert!(wia1.len() <= 400, "the payload is {} bytes", wia1.len());
assert!(wai::container::check_int_payload_framing(c::CAP_AUDIO_INT_CODEC, &wia1).is_ok(), "it parses");
let (r, largest) = largest_in(|| decode_int(c::CAP_AUDIO_INT_CODEC, &wia1, &aud));
let bound = FEW_KB.max(4 * model.len());
assert!(largest <= bound, "the 384-byte WIA1: refusing it allocated {largest} bytes at once, over {bound}");
assert_eq!(r.map(|_| ()).map_err(|e| e.code()), Err("geometry_mismatch"), "the 384-byte WIA1");
assert_eq!(wai::int_payload::declared_cost(c::CAP_AUDIO_INT_CODEC, &wia1, &aud).map(|_| ()).map_err(|e| e.code()),
Err("geometry_mismatch"), "the declared cost refuses it too");
// The tools' JSON form, read by int_video::VideoJson (the converter's and the JSON
// decode's reader): a header past the caps is refused before anything is sized
// from it, on every target. A side of 2^31 made the frame size overflow and the
// reader panic; sixteen frames of 4096 × 4096 at block 1, each with an empty list,
// were padded to 2^28 motion vectors before any cap was checked.
use serde_json::json;
let js = |side: u64, nf: usize| json!({
"block": 1, "q": 1, "offset": 0, "cdf": [0, 1, 65_536], "n_frames": nf, "h": side, "w": side,
"mvs": vec![json!([]); nf], "rans_b64": "AAAAAAAAAAA="
});
for (what, j) in [("h = w = 2^31", js(1 << 31, 2)), ("2^28 padded motion vectors", js(4096, 16))] {
let (r, largest) = largest_in(|| std::panic::catch_unwind(|| (
wai::int_video::VideoJson::from_value(&j).is_err(),
wai::int_payload::wiv1_from_json(&j, None).map(|_| ()).map_err(|e| e.code()),
wai::int_video::decode(&j).is_err(),
)));
assert_eq!(r.expect("the JSON reader must not panic"), (true, Err("field_range"), true), "{what}");
assert!(largest <= FEW_KB, "{what}: refusing it allocated {largest} bytes at once");
}
}