From 6b4be28d24aec40d1d77111d2c7381e01f5d4229 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 19:25:22 +0200 Subject: [PATCH] docs(client): write down why the CPU rung is not process-isolated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #97's frame-context floor closes the one rav1d abort we hit and can prove. It does not make the rung panic-proof and nothing at that call site can, because rav1d's public surface is dav1d's C ABI: any reachable panic crosses `extern "C"` as `panic_cannot_unwind` and becomes `abort()`, past every `catch_unwind`, rung demotion and typed refusal we have. Counted across rav1d 1.1.0's 60 source files: 285 `unwrap()`, 214 `assert!`, 19 `unreachable!`, 11 `expect()`, 10 `panic!`. 539 sites that end the client if a stream can reach them. #97 fixed one of them. Process isolation is the only defence that actually works, and this records the decision NOT to build it, with the reasoning, so it is not re-argued from scratch each time someone reads that number: * the defect is upstream's and is one line (memorysafety/rav1d#1497, filed 2026-08-07 with the fix and a reproducer; still open, no PR, as of today); * 539 is an unbounded number, not a risk estimate — none of those sites is known reachable from a punktfunk stream, and the honest next step is to fuzz the rung and find out, which is cheap, rather than buy insurance, which is not; * the cost lands on the video path across Linux, Windows and Android (the Apple clients decode through VideoToolbox and never reach this code), each needing its own shared-memory frame transport, child lifecycle and backpressure, and it adds a scheduling boundary to the slowest rung on the ladder while zero-copy is a hard requirement; * an abort here costs a session that was already degraded — this rung exists because the GPU rungs failed first. The trigger to revisit is named as an event rather than a feeling: a SECOND distinct abort in the field, or a fuzzer finding a reachable panic. Either makes it a class of bugs instead of one, and a class is what would justify the architecture. Documentation only — no behaviour change. --- crates/pf-client-core/src/video_software.rs | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/crates/pf-client-core/src/video_software.rs b/crates/pf-client-core/src/video_software.rs index bd37a6dc..718f7729 100644 --- a/crates/pf-client-core/src/video_software.rs +++ b/crates/pf-client-core/src/video_software.rs @@ -45,6 +45,46 @@ //! no equivalent here. rav1d gets the machine's cores, and **at least two frame contexts**; //! [`Av1Software::new`] carries the whole argument, because "at least two" is not a //! performance choice but the difference between an error and `abort()`. +//! +//! # Why this rung is NOT process-isolated +//! +//! The frame-context floor closes the one abort we hit and can prove. It does not make the +//! rung panic-proof, and nothing at this call site can: rav1d exposes dav1d's C ABI, every +//! internal `rav1d_*` entry point is `pub(crate)`, so any reachable panic crosses +//! `extern "C"` as `panic_cannot_unwind` → `abort()`. No `catch_unwind`, no rung demotion +//! and no [`NoSoftwareRung`] refusal can contain it. Counted in rav1d 1.1.0's 60 source +//! files: 285 `unwrap()`, 214 `assert!`, 19 `unreachable!`, 11 `expect()`, 10 `panic!` — +//! 539 sites that are an `abort()` if a stream can reach them. #97 fixed ONE. +//! +//! Isolating the decoder in its own process is the only defence that actually works, and +//! it is deliberately NOT taken. The decision, so it is not re-litigated from scratch: +//! +//! * **The defect is a dependency's, and it is one line.** memorysafety/rav1d#1497 was +//! filed 2026-08-07 with the fix (`is_some_and` for the `unwrap`) and a reproducer. +//! Paying a permanent architectural tax to route around a bug that costs upstream one +//! line is the wrong trade while that line is still plausibly coming. +//! * **The residual risk is real but unquantified.** 539 panic sites is a scary number +//! and a meaningless one: not one of them is known to be reachable from a punktfunk +//! stream. The honest next step is to MEASURE reachability — fuzz this rung with +//! truncated, reordered and bit-flipped AUs and see whether any input aborts — not to +//! buy insurance against a number nobody has bounded. That is cheap; this is not. +//! * **The cost lands on the video path, and on three platforms.** pf-client-core builds +//! into the Linux, Windows and Android clients (the Apple clients decode through +//! VideoToolbox and never reach here). Each needs its own shared-memory transport for +//! `CpuPlanarFrame`s, its own child lifecycle, crash detection and restart, and its own +//! backpressure — and it adds a scheduling boundary to the rung that is ALREADY the +//! slowest one on the ladder. Zero-copy is a hard requirement here; an IPC hop that +//! copies frames would be rejected on its own terms. +//! * **What an abort actually costs is bounded.** This rung is reached because the GPU +//! rungs already failed, so the session is degraded before rav1d sees a byte. Losing +//! the process loses a session the user was going to have a bad time in regardless. +//! That is bad, and it is not the same as losing a working session. +//! +//! **Revisit when the calculus changes, which is a specific event, not a feeling:** a +//! SECOND distinct abort observed in the field, or a fuzzer finding a reachable panic. +//! Either turns this from one upstream bug into a class of them, and a class is what +//! justifies isolation. Until then the floor plus the upstream fix is the proportionate +//! answer, and the fuzzing is the work that would tell us we were wrong. use crate::video::{CpuPlanarFrame, RungLoss}; use crate::video_color::ColorDesc;