Skip to content

[FIX] file_functions: check the seek that can fail instead of one that cannot - #2326

Open
cfsmp3 wants to merge 1 commit into
masterfrom
fix/buffered-read-opt-dead-seek-guard
Open

[FIX] file_functions: check the seek that can fail instead of one that cannot#2326
cfsmp3 wants to merge 1 commit into
masterfrom
fix/buffered-read-opt-dead-seek-guard

Conversation

@cfsmp3

@cfsmp3 cfsmp3 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #2325, which flagged this line and deliberately left it alone.

The guard could not fire

LLONG op, np;
op = LSEEK(ctx->infd, 0, SEEK_CUR);   // Get current pos
if (op + bytes < 0)                   // Would mean moving beyond start of file: Not supported
	return 0;
np = LSEEK(ctx->infd, bytes, SEEK_CUR);
i = (int)(np - op);

bytes is a size_t, so op + bytes is evaluated unsigned and never goes negative — the same family as the underflow #2322 fixed. And it could not matter anyway: every caller reaches here with a forward distance, so a seek from a valid position cannot land before the start of the file.

What can actually fail

LSEEK returns −1 on error. Neither result was checked, so np - op was computed from two error values and reported as a byte count.

Today that lands on (-1) - (-1) == 0, the do/while condition then ends the loop, and the function returns copied — so the observable behaviour is correct by arithmetic accident. Nothing tells the next reader that, and the guard sitting above it implies the failure is already handled.

if (op < 0)
	return copied;
np = LSEEK(ctx->infd, bytes, SEEK_CUR);
if (np < 0)
	return copied;
i = (int)(np - op);

copied rather than 0: it is what the normal path returns a few lines down, and bytes already handed to the caller should not be forgotten because a later seek failed.

Testing

A/B against a binary built from current master (3af3fc22):

result
59 samples, --autoprogram --out=srt --latin1 (99 output files) byte-identical, exit codes match
36 invocations targeting this branch — --startat/--endat, --no-bufferinput, --dru 30 identical, 6 produced no output on either side, 0 differences

Builds with no compiler messages; clang-format clean.

Worth stating how that second row was nearly misread: comparing the two runs with cmp alone reported "6 differences", because cmp fails the same way whether the contents differ or the file is absent — and with those options neither side writes one. The counts above separate identical, both produced nothing, one-sided, and content differs, because only the last is evidence of anything.

… cannot

buffered_read_opt()'s seek branch guarded against moving before the start of
the file with "op + bytes < 0". That could not fire: bytes is unsigned, so the
sum was evaluated unsigned and never went negative, and every caller passes a
forward distance anyway.

What can fail is LSEEK itself. On error it returns -1, and the code then
computed "np - op" from two error values and reported the result as a byte
count. Today that lands on 0 and the loop exits, so the observable behaviour is
unchanged -- but only by arithmetic accident, and the next reader has no way to
tell that the guard above it was inert.

Check what actually fails, and return the bytes copied so far rather than
inventing a distance from two failures.

Noted while reviewing the MSVC narrowing warnings for #2325, which flagged this
line as dead rather than wrong and left it out of that change deliberately.
@ccextractor-bot

Copy link
Copy Markdown
Collaborator
CCExtractor CI platform finished running the test files on linux. 167/237 tests matched the approved output:
Report Name Tests Passed
Broken 9/13
CEA-708 2/14
DVB 0/7
DVD 3/3
DVR-MS 2/2
General 22/27
Hardsubx 1/1
Hauppage 3/3
MP4 3/3
NoCC 10/10
Options 69/86
Teletext 0/21
WTV 12/13
XDS 31/34

70 tests do not match the approved output. That is the pass/fail verdict. Whether this branch caused it is a separate question, answered below.


Compared with the tip of mastertest 9494, commit 3af3fc2:

  • 1 pass there and fail here
  • 0 fail there and pass here
  • 0 fail on both, with different output
  • 69 fail on both, byte for byte the same

Pass there, fail here:


Compared with the commit this branch was cut from: the same run as the tip of master (test 9494), so the comparison above already covers it.


This branch changes the behaviour of 1 test(s) relative to the tip of master. Those are the ones worth looking at; anything else in the list fails the same way on both sides.

@ccextractor-bot

Copy link
Copy Markdown
Collaborator
CCExtractor CI platform finished running the test files on windows. 167/237 tests matched the approved output:
Report Name Tests Passed
Broken 9/13
CEA-708 2/14
DVB 0/7
DVD 3/3
DVR-MS 2/2
General 22/27
Hardsubx 1/1
Hauppage 3/3
MP4 3/3
NoCC 10/10
Options 69/86
Teletext 0/21
WTV 12/13
XDS 31/34

70 tests do not match the approved output. That is the pass/fail verdict. Whether this branch caused it is a separate question, answered below.


Compared with the tip of mastertest 9495, commit 3af3fc2:

  • 1 pass there and fail here
  • 0 fail there and pass here
  • 0 fail on both, with different output
  • 69 fail on both, byte for byte the same

Pass there, fail here:


Compared with the commit this branch was cut from: the same run as the tip of master (test 9495), so the comparison above already covers it.


This branch changes the behaviour of 1 test(s) relative to the tip of master. Those are the ones worth looking at; anything else in the list fails the same way on both sides.

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.

2 participants