Skip to content

Reject out-of-range seeks without decoding to EOF#69

Merged
kmatzen merged 1 commit into
mainfrom
fix/seek-bounds-check
Jul 19, 2026
Merged

Reject out-of-range seeks without decoding to EOF#69
kmatzen merged 1 commit into
mainfrom
fix/seek-bounds-check

Conversation

@kmatzen

@kmatzen kmatzen commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Fixes #67.

frame_count_ was populated in open() and never read in seek(). A knowably impossible target was therefore discovered only by scanning forward and decoding every remaining frame until read() failed — O(frames remaining) of full decode and colour conversion for a request that could be rejected on arrival.

if (frame_count_ > 0 && frame_number >= frame_count_) {
    return false;
}

The guard stays conditional on frame_count_ > 0 because -1 means the container reports no count (common with MKV and some MP4), and there the scan really is the only way to find out.

The test asserts position, not speed

Timing assertions are flaky, so the new test checks the observable consequence instead: a rejected seek should leave the reader where it was, whereas the old scan consumed the rest of the file first.

Verified it catches the old behaviour — reverting VideoReader.cpp fails with:

tests/test_video_reader.cpp:281: FAILED:
with expansion:  60 == 5

The reader had walked to frame 60 on a 60-frame fixture as a side effect of a failed seek from frame 5.

Sentinel fix

cleanup() set frame_count_ = 0 while open() uses -1 and getFrameCount() documents -1 for "unknown". A closed reader reported a legitimate-looking count of zero. Now -1 in both places; covered by a test that also fails on the old code (0 == -1).

No existing code read getFrameCount() apart from the Python binding's frame_count property, so nothing depended on the old value.

Full suite: 41 cases, 1135 assertions, all passing locally.

🤖 Generated with Claude Code

frame_count_ was populated in open() and never read in seek(), so a
knowably impossible target was discovered only by scanning forward and
decoding every remaining frame until read() failed -- O(frames remaining)
of full decode and colour conversion for a request that could be rejected
immediately.

The guard stays conditional on frame_count_ > 0 because -1 means the
container reports no count (common with MKV and some MP4), and then the
scan really is the only way to find out.

The observable difference is position, which is what the new test asserts:
rejecting early leaves the reader where it was, whereas the old scan
consumed the rest of the file first. Without the fix it reports frame 60
on a 60-frame fixture after a failed seek from frame 5.

Also fixes the sentinel: cleanup() set frame_count_ to 0 while open() uses
-1 and getFrameCount() documents -1 for "unknown", so a closed reader
reported a legitimate-looking count of zero.

Fixes #67

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kmatzen
kmatzen merged commit 692733b into main Jul 19, 2026
4 checks passed
@kmatzen
kmatzen deleted the fix/seek-bounds-check branch July 19, 2026 00:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

seek() ignores frame_count_ and decodes the whole file for an out-of-range target

1 participant