57be4fe333e2cd6ec361fa3b9ff5404759dff426 [FFmpeg] Remove negative timestamp check for codec delay
Triage note: Robustness fix for attacker-controlled media parsing; worst-case is a/v sync drift plus a debug-build DCHECK abort. Not a corruption primitive in release builds.
The bug at a glance
This is watch-tier, not patch-now, because the assertion being deleted is a DCHECK, which is compiled out of release/stable Chrome entirely. In an official release build the removed line was already a no-op, so no memory-safety property, bounds check, or trust boundary changes for real users. The defect it guarded against is a debug/ASAN/fuzzer-only abort: adversarial media whose codec_delay combines with a negative container start_time can drive a demuxed audio buffer’s timestamp negative, tripping DCHECK_GE and killing the process in instrumented builds. The author states plainly the worst-case functional consequence is a/v sync drift, not corruption. It rates low/CVSS 3.1 rather than zero only because timestamp handling feeds trimming and discard-padding math downstream, and a negative timestamp propagating into duration/trim arithmetic is the kind of latent-safety concern worth tracking even though no out-of-bounds or lifetime consequence is demonstrated here.
Defenders should read this as a fuzzer-hardening / invariant-relaxation change, not as a shipped-vulnerability fix. Chrome’s media fuzzers exercise ffmpeg_demuxer with malformed containers; one of them found input (fixed bug 475585411) that produces a legitimately negative post-adjustment timestamp when codec_delay is present and the container advertises a negative start_time. The old code asserted the timestamp could never be negative after the adjustment; that assumption was simply wrong for attacker-controlled input. The engineer tried to recompute start_time from codec_delay to keep timestamps non-negative, but FFmpeg does not apply -FramesToTime(codec_delay) consistently, causing incorrect first/last-packet trimming, so the safer choice was to drop the assertion and let the (possibly negative) timestamp flow through. The security-relevant question for auditors is not this line but whether any downstream consumer of buffer->timestamp() assumes non-negativity in a memory-unsafe way.
Root cause
In FFmpegDemuxerStream, decoded audio buffers get a timestamp derived from the raw stream timestamp adjusted by the demuxer’s start_time. For audio streams that declare a codec_delay (encoder priming, e.g. Opus/AAC) and where the container’s start_time is negative, the code rebases the buffer: buffer->set_timestamp(stream_timestamp - demuxer_->start_time()). Subtracting a negative start_time normally pushes the timestamp forward (non-negative). The prior code encoded that expectation as DCHECK_GE(buffer->timestamp(), base::TimeDelta()).
The root cause is that this invariant is not guaranteed for adversarial input. With a crafted stream_timestamp and start_time, and the interaction of codec_delay-based priming, the subtraction can still yield a value below zero. Because DCHECK aborts in debug/ASAN/fuzzer configurations, the fuzzer treats this as a crash, generating noise (and a theoretical DoS signal in instrumented builds only). The commit removes the DCHECK and adds a comment explaining that FFmpeg’s own start_time handling relative to codec_delay is inconsistent, so recomputing an idealized non-negative start_time was rejected in favor of trusting the direct adjustment.
Net effect: release behavior is unchanged; instrumented-build stability is restored; and the codebase now explicitly tolerates negative adjusted audio timestamps in this narrow path, with the only stated functional risk being audio/video synchronization drift on such pathological media.
Key code
media/filters/ffmpeg_demuxer.cc: the removed non-negativity assertion on the codec-delay/negative-start_time path
if (is_audio && audio_decoder_config().codec_delay() &&
demuxer_->start_time().is_negative()) {
// Note: FFmpeg doesn't seem to always use -FramesToTime(codec_delay()) as
// the start_time for unknown reasons, so we use the start time directly.
buffer->set_timestamp(stream_timestamp - demuxer_->start_time());
DCHECK_GE(buffer->timestamp(), base::TimeDelta()); // <-- removed
}
Patch walkthrough
media/filters/ffmpeg_demuxer.cc— Deletes the single line DCHECK_GE(buffer->timestamp(), base::TimeDelta()) inside the is_audio + codec_delay + negative start_time branch, and adds an explanatory comment about why the direct start_time subtraction is used instead of a codec-delay-derived value. No control-flow, allocation, or bounds logic changes; the branch still performs the same set_timestamp() call.
Background
DCHECK vs CHECK — DCHECK assertions are active only in debug builds and instrumented (e.g. ASAN/fuzzer) builds; they are compiled out of official release/stable Chrome. Removing a DCHECK therefore does not change what end users run - it changes only what the fuzzer and developers observe.
codec_delay / encoder priming — Codecs like Opus and AAC emit priming samples that must be discarded on playback. codec_delay expresses that priming; combined with container edit lists it interacts with start_time to place the first real sample at t=0.
Negative container start_time — Some containers legitimately signal a negative start_time (e.g. to represent pre-roll). FFmpeg’s own reconciliation of start_time with codec delay is inconsistent, which is why Chrome subtracts the reported start_time directly rather than recomputing it.
Fixed: 475585411 — The crbug referenced is a ClusterFuzz-class crash bug (DCHECK failure), consistent with a fuzzer-only stability finding rather than a memory-corruption report. No CVE assigned; no [M###] merge/backport marker is present in the commit.
Vulnerability window
- Discovery — Fuzzer-generated media (bug 475585411) produced a demuxed audio buffer whose adjusted timestamp went negative, tripping DCHECK_GE in an instrumented build.
- Attempted fix — The author tried using -FramesToTime(codec_delay()) as the start_time to keep timestamps non-negative, but FFmpeg did not apply it consistently, causing incorrect trimming of first/last packets.
- Landed fix — Committed 2026-01-22 (Dale Curtis), reviewed by Jordan Bayles, at position refs/heads/main@{#1573212}: the DCHECK was removed and the direct start_time subtraction retained, tolerating negative timestamps on this path.
Proof of concept
No exploit is demonstrated and none is possible from this change; the commit ships no test. To reproduce the original DCHECK crash (instrumented builds only), a defender would craft or fuzz an audio container whose stream declares a non-zero codec_delay and whose start_time is negative, arranged so that stream_timestamp - start_time evaluates below zero for at least the first buffer, then feed it through the media pipeline in an ASAN/debug build. In release/stable this yields at most audio/video sync drift. There is no memory-safety trigger to weaponize.
Exploitation
- Reach — Any web content that can play audio (a / element, MSE, or WebCodecs feeding the ffmpeg demuxer path) can supply crafted media, so the code is remotely reachable in principle.
- Trigger — Only an instrumented build aborts on the negative timestamp; stable Chrome does not, so there is no crash or corruption primitive for real users - the ceiling is a debug-only DoS.
- Impact ceiling — Stated worst case is a/v sync drift. No out-of-bounds read/write, no lifetime issue, no info leak is shown; downstream trim/discard math would need an independent bug to convert a negative timestamp into memory unsafety.
Detection & hunting
For defenders and detection engineers:
- Fuzzer/crash telemetry — In ASAN/debug fleets, a DCHECK_GE failure at ffmpeg_demuxer.cc near the codec_delay branch indicates the pre-fix condition; post-fix these stop, so a regression re-introducing the assertion would resurface as crash noise.
- Playback anomalies — User-visible audio/video desynchronization on specific media with encoder priming and negative start_time is the only production-observable symptom; correlate with unusual container start_time values in media logs.
Audit directions
- Downstream timestamp consumers —
- Sibling DCHECK-guarded invariants —