fix(qr): accept fountain parts with seqnum above sequence count - #2700
Conversation
The #2608 hardening rejected multipart UR frames whose sequence number exceeds the declared sequence count. Animated URs are fountain-coded (BCR-2020-005): after the pure fragments 1..N play, the stream keeps emitting mixed parts N+1, N+2, ... so late-joining decoders can recover. Since the animation usually loops before the camera sees it, nearly every captured frame tripped the check, the reader reset on the error, and scanning failed immediately with 'UR processing failed'. Keep the sequence-count and message-size caps (they bound the decoder buffers) but let seqNum run unbounded, and add a regression test that decodes a genuine encoder stream the camera joins mid-animation.
|
Claude finished @ben-kaufman's task in 5m 42s —— View job Review: fix(qr): accept fountain parts with seqnum above sequence count
Verdict: the fix is correct and I found no blocking issues. One non-blocking security observation and one test-robustness issue worth addressing, below.
Correctness of the fix — confirmedThe removed I also verified the fix doesn't undo #2608:
Finding 1 — non-blocking security note (
|
Summary
URQR scanning is broken in v6.13.0 (current Latest release): scanning any animated UR fails immediately with "UR processing failed", typically on the first captured frame. Regression introduced by ef78955 (the security-audit hardening for #2608), first shipped in v6.13.0 — 6.12.x is unaffected.
Root cause
The #2608 hardening added a multipart frame validation that rejects frames whose sequence number exceeds the declared sequence count (
sequenceNumber > sequenceCount).Animated URs are fountain-coded (BCR-2020-005): after the pure fragments
1..Nhave played, the stream keeps emitting mixed partsN+1,N+2, … indefinitely — that unbounded sequence is exactly what allows a decoder that missed frames (or joined mid-animation) to recover. Since the animation has usually been looping before the camera points at it, nearly every captured frame hasseqNum > seqCount, so the very first frame threwUrSequenceLimitExceeded, the reader reset in the catch path, and the scan could never complete.The existing roundtrip test didn't catch this because it feeds parts
1..Nin order, whereseqNum ≤ seqCountalways holds.Fix
Drop the
sequenceNumber > sequenceCountcondition inUrQrReader._validateMultipartFrame. The guards that actually bound the decoder's resources stay in place:sequenceCount > maxMultipartParts(1000) — caps the part countdata.length > maxMultipartMessageSize(1 MB) — caps frame sizesequenceNumber < 1— rejects malformed framesSo the #2608 DoS protection is preserved; only the spec-violating check is removed.
Tests
test/core_test/urqr/ur_qr_reader_test.dart: builds a genuine fountain stream with the realUREncoder(including mixed parts pastisComplete, whichUrQrGeneratorcannot produce) and decodes a stream the camera joins mid-animation — the exact reported scenario. Verified to fail against the pre-fix code and pass with the fix.test/core_test/urqrandtest/security_audittests pass (106 tests), including the Security (Medium, QR F-2): unbounded UR sequence lengths can exhaust device memory #2608 sequence-bound test.make analyzeclean,dart fix --dry-runclean.