format-probes: two adjacent start codes no longer panic the AVS3 probe #586

Merged
dcharlot merged 1 commit from wai/avs3-probe-panic into main 2026-11-02 20:12:45 -05:00
3 changed files with 44 additions and 1 deletions

View file

@ -6,6 +6,12 @@ wire format may change and such changes are recorded here.
## Unreleased
### A probe no longer panics on two adjacent start codes
- `wai.video.avs3` and `wai.audio.avs3`: a payload with a start code prefix directly after another (`00 00 01 00 00 01`)
made the unit split slice backwards and panic, so a hostile payload could stop whatever probed it. It is now the empty
unit format-probes §8.2 reads, and the payload is refused. A test now gives every probe 30,000 payloads of hostile bytes.
### A third party's hop has a signed record: what was sent, what came back, and what the provider bills
- **The third-party hop record** ([extensions/route-record.md](extensions/route-record.md) §6.2, §7.2 step 9),

View file

@ -164,6 +164,41 @@ mod tests {
assert_eq!(probe(CAP_IMAGE_PNG, &[]), Err(Refusal::NoProbe));
}
/// A payload is whatever arrives: no probe may panic on it, whatever its bytes.
#[test]
fn no_bytes_make_a_probe_panic() {
let mut x = 0x9e37_79b9_7f4a_7c15u64;
let mut next = move || {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
x
};
// What the probes' framing is made of, so that noise reaches past their first checks.
const EDGE: [u8; 14] = [0, 0, 0, 1, 2, 3, 0x40, 0x42, 0x44, 0xb0, 0xb3, 0xff, 0x10, 0x7c];
const STARTS: [&[u8]; 8] = [
b"", b"\x00\x00\x01", b"\x00\x00\x01\x00\x00\x01", b"\x00\x00\x01\xb0", b"\x00\x00\x01\x40\x01",
b"\x00\x00\x01\x7c", b"\xff\x10\xff\x50", b"\xc0\x01\x10",
];
for round in 0..30_000u32 {
let mut b = STARTS[round as usize % STARTS.len()].to_vec();
for _ in 0..next() as usize % 128 {
let r = next();
b.push(if r & 1 == 0 { EDGE[(r >> 8) as usize % EDGE.len()] } else { (r >> 16) as u8 });
}
for &(c, _) in PROBES {
let _ = probe(c, &b);
}
}
}
#[test]
fn adjacent_start_codes_are_refused_and_do_not_panic() {
for c in [CAP_VIDEO_AVS3, CAP_AUDIO_AVS3, CAP_VIDEO_HEVC, CAP_VIDEO_VVC, CAP_VIDEO_LCEVC] {
assert!(probe(c, b"\x00\x00\x01\x00\x00\x01\xb0\x00\x00\x01").is_err(), "{c}");
}
}
#[test]
fn codes_are_distinct_and_read_back() {
for r in Refusal::ALL {

View file

@ -37,7 +37,9 @@ pub fn units(b: &[u8]) -> Result<Vec<(u8, &[u8])>, Refusal> {
let mut out = Vec::new();
for (k, &s) in starts.iter().enumerate() {
let end = if k + 1 < starts.len() { starts[k + 1] - 3 } else { b.len() };
out.push((b[s], &b[s + 1..end]));
// A prefix directly after a prefix: that unit's start code is the next prefix's first
// byte, and it holds nothing.
out.push((b[s], b.get(s + 1..end).unwrap_or(&[])));
}
Ok(out)
}