Skip to content

[FIX] Segfault on files carrying both teletext and DVB subtitles - #2319

Merged
cfsmp3 merged 1 commit into
masterfrom
fix/encoder-list-uninitialized-fallthrough
Aug 13, 2026
Merged

[FIX] Segfault on files carrying both teletext and DVB subtitles#2319
cfsmp3 merged 1 commit into
masterfrom
fix/encoder-list-uninitialized-fallthrough

Conversation

@cfsmp3

@cfsmp3 cfsmp3 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

ccextractor --out=srt sample.mpg segfaults on any recording that carries both a teletext stream and a DVB subtitle stream. No special flags are needed and every output format is affected.

Root cause

update_encoder_list_cinfo() declares enc_ctx uninitialised and then uses it as the list_for_each_entry() iterator. For a DVB stream carrying a language, the search loop continues past every encoder whose dvb_lang does not match, so it can run to completion without returning. When the encoder list is non-empty, none of the creation branches below run either, and the function falls through to

	// DVB related
	enc_ctx->prev = NULL;

with enc_ctx still holding the value the loop terminated on — the list head reinterpreted as an encoder_ctx. That bogus pointer is written to and then returned. general_loop.c:1358 later does dvb_enc->timing = dvb_dec->timing on it and faults.

Under gdb the faulting address is exactly &dvb_enc->timing, and the instruction is a store:

=> 0x...<process_non_multiprogram_general_loop+10404>:	mov %rdx,0x50(%rax)
si_addr        = 0x7d2ff31dfea0
&dvb_enc->timing = 0x7d2ff31dfea0

The condition arises whenever a DVB PID is not the stream the main path selected — i.e. a DVB stream sitting alongside teletext.

Why CI never caught it

Restricting to a single PID avoids it entirely, and the one affected sample in the regression suite (85c7fc1ad7, sample 19) is only ever run as --datapid 5603 …. Plain extraction on the same file crashes.

Fix

Clear the iterator after the search loop so "not found" is distinguishable from "found", and give such a stream its own per-language encoder — which is what the multi-DVB path already does for extra DVB PIDs. The existing dvb_pid_count >= 2 test is kept as an OR, so a recording whose only caption stream is a single DVB PID still reaches the standard path with an empty encoder list and keeps its plain output filename. A NULL guard before the tail keeps the fall-through honest.

Both streams are now extracted into separate, well-formed files rather than being merged into one file with two interleaved cue numberings.

Verification

Scanned all 162 local media samples with a release build of master and of this branch:

master this branch
85c7fc1ad7…mpg SIGSEGV out.srt + out_eng.srt
f1422b8bfe…ts SIGSEGV out.srt + out_spa.srt

Each produced file has monotonic, unique cue indices and monotonic timestamps.

Regression checks:

  • 45 samples byte-identical to master (--out=srt, whole output directory compared).
  • Multi-language DVB unchanged04e47919de59…ts still yields out_chi.srt + out_chs.srt, byte-identical to master.
  • Filename compatibility preserved — a recording whose only stream is DVB still produces plain out.srt on both master and this branch.
  • ASan: the SEGV is gone. The remaining report is the pre-existing 32-byte-per-encoder leak in init_encoder (ccx_encoders_common.c:818) that master also has; this branch shows it twice simply because it now creates a second encoder.

Not fixed here

A third sample, 5f6dfe831e35…wtv, also segfaults on master but through an unrelated path — an unsigned underflow in buffered_seek() reaching buffered_read(). That is a separate bug and will get its own PR.

update_encoder_list_cinfo() declares enc_ctx uninitialised and uses it as the
list_for_each_entry() iterator. For a DVB stream carrying a language, the search
loop continues past every encoder whose dvb_lang does not match, so it can finish
without returning. When the encoder list is non-empty none of the creation
branches below run either, and the function falls through to

    enc_ctx->prev = NULL;

with enc_ctx still holding the value the loop terminated on: the list head
reinterpreted as an encoder_ctx. That pointer is written to, and later
general_loop.c does dvb_enc->timing = dvb_dec->timing on it, which segfaults.

It reproduces on any recording that carries both a teletext stream and a DVB
subtitle stream, in every output format, with no extra flags:

    ccextractor --out=srt  sample.mpg   -> SIGSEGV
    ccextractor --out=webvtt sample.ts  -> SIGSEGV

Restricting to one PID (--datapid) avoids it, which is why the regression suite
never caught it: the only affected sample it runs passes --datapid.

Clear the iterator after the loop so "not found" is distinguishable, and give
such a stream its own per-language encoder, which is what the multi-DVB path
already does. The existing dvb_pid_count >= 2 test stays as an OR so a recording
whose only caption stream is a single DVB PID keeps its plain output filename --
that case reaches the standard path with an empty encoder list. A NULL guard
before the tail keeps the fall-through honest if some future path adds one.

Both streams are now extracted into separate, well-formed files (out.srt plus
out_eng.srt) rather than one file with two interleaved numberings.
@ccextractor-bot

Copy link
Copy Markdown
Collaborator
CCExtractor CI platform finished running the test files on linux. Below is a summary of the test results, when compared to test for commit 6077cf5...:
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

Your PR breaks these cases:

NOTE: The following tests have been failing on the master branch as well as the PR:


It seems that not all tests were passed completely. This is an indication that the output of some files is not as expected (but might be according to you).

Check the result page for more info.

@ccextractor-bot

Copy link
Copy Markdown
Collaborator
CCExtractor CI platform finished running the test files on windows. Below is a summary of the test results, when compared to test for commit c328108...:
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

Your PR breaks these cases:

NOTE: The following tests have been failing on the master branch as well as the PR:


It seems that not all tests were passed completely. This is an indication that the output of some files is not as expected (but might be according to you).

Check the result page for more info.

@cfsmp3
cfsmp3 merged commit 128175e into master Aug 13, 2026
45 of 48 checks passed
@cfsmp3
cfsmp3 deleted the fix/encoder-list-uninitialized-fallthrough branch August 13, 2026 14:53
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