mirror of
https://github.com/CCExtractor/ccextractor.git
synced 2026-07-08 18:06:30 +00:00
fix(memory): Fix use-after-free in Teletext and uninitialized variables
This commit fixes several Valgrind-detected memory issues:
1. Use-after-free in Teletext during PAT changes:
- When parse_PAT() calls dinit_cap() to reinitialize stream info,
it freed the Teletext context but dec_ctx->private_data still
pointed to the freed memory
- Fixed by NULLing out dec_ctx->private_data in dinit_cap() when
freeing shared codec private data
- Also added NULL check in process_data() before calling teletext
functions to gracefully handle freed contexts
2. Uninitialized variables in general_loop():
- stream_mode, get_more_data, ret, and program_iter were declared
without initialization
- While logically set before use, Valgrind tracked them as
potentially uninitialized through complex control flow
- Fixed by initializing all variables at declaration
These fixes eliminate millions of Valgrind errors in teletext tests
(tests 78, 80) and uninitialized value warnings (tests 67, 84, 86).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -669,24 +669,33 @@ int process_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, str
|
||||
{
|
||||
// telxcc_update_gt(dec_ctx->private_data, ctx->demux_ctx->global_timestamp);
|
||||
|
||||
/* Process Teletext packets even when no encoder context exists (e.g. -out=report).
|
||||
This enables tlt_process_pes_packet() to detect subtitle pages by populating
|
||||
the seen_sub_page[] array inside the teletext decoder. */
|
||||
int sentence_cap = enc_ctx ? enc_ctx->sentence_cap : 0;
|
||||
/* Check if teletext context is still valid (may have been freed by dinit_cap
|
||||
during PAT change while stream was being processed) */
|
||||
if (!dec_ctx->private_data)
|
||||
{
|
||||
got = data_node->len; // Skip processing, context was freed
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Process Teletext packets even when no encoder context exists (e.g. -out=report).
|
||||
This enables tlt_process_pes_packet() to detect subtitle pages by populating
|
||||
the seen_sub_page[] array inside the teletext decoder. */
|
||||
int sentence_cap = enc_ctx ? enc_ctx->sentence_cap : 0;
|
||||
|
||||
ret = tlt_process_pes_packet(
|
||||
dec_ctx,
|
||||
data_node->buffer,
|
||||
data_node->len,
|
||||
dec_sub,
|
||||
sentence_cap);
|
||||
ret = tlt_process_pes_packet(
|
||||
dec_ctx,
|
||||
data_node->buffer,
|
||||
data_node->len,
|
||||
dec_sub,
|
||||
sentence_cap);
|
||||
|
||||
/* If Teletext decoding fails with invalid data, abort processing */
|
||||
if (ret == CCX_EINVAL)
|
||||
return ret;
|
||||
/* If Teletext decoding fails with invalid data, abort processing */
|
||||
if (ret == CCX_EINVAL)
|
||||
return ret;
|
||||
|
||||
/* Mark processed byte count */
|
||||
got = data_node->len;
|
||||
/* Mark processed byte count */
|
||||
got = data_node->len;
|
||||
}
|
||||
}
|
||||
else if (data_node->bufferdatatype == CCX_PRIVATE_MPEG2_CC)
|
||||
{
|
||||
@@ -1030,11 +1039,11 @@ int process_non_multiprogram_general_loop(struct lib_ccx_ctx *ctx,
|
||||
int general_loop(struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
enum ccx_stream_mode_enum stream_mode;
|
||||
enum ccx_stream_mode_enum stream_mode = CCX_SM_ELEMENTARY_OR_NOT_FOUND;
|
||||
struct demuxer_data *datalist = NULL;
|
||||
struct demuxer_data *data_node = NULL;
|
||||
int (*get_more_data)(struct lib_ccx_ctx *c, struct demuxer_data **d);
|
||||
int ret;
|
||||
int (*get_more_data)(struct lib_ccx_ctx *c, struct demuxer_data **d) = NULL;
|
||||
int ret = 0;
|
||||
int caps = 0;
|
||||
|
||||
uint64_t min_pts = UINT64_MAX;
|
||||
@@ -1115,7 +1124,7 @@ int general_loop(struct lib_ccx_ctx *ctx)
|
||||
else
|
||||
{
|
||||
struct cap_info *cinfo = NULL;
|
||||
struct cap_info *program_iter;
|
||||
struct cap_info *program_iter = NULL;
|
||||
struct cap_info *ptr = &ctx->demux_ctx->cinfo_tree;
|
||||
struct encoder_ctx *enc_ctx = NULL;
|
||||
list_for_each_entry(program_iter, &ptr->pg_stream, pg_stream, struct cap_info)
|
||||
|
||||
@@ -266,6 +266,8 @@ int update_capinfo(struct ccx_demuxer *ctx, int pid, enum ccx_stream_type stream
|
||||
void dinit_cap(struct ccx_demuxer *ctx)
|
||||
{
|
||||
struct cap_info *iter;
|
||||
struct lib_ccx_ctx *lctx = (struct lib_ccx_ctx *)ctx->parent;
|
||||
|
||||
while (!list_empty(&ctx->cinfo_tree.all_stream))
|
||||
{
|
||||
iter = list_entry(ctx->cinfo_tree.all_stream.next, struct cap_info, all_stream);
|
||||
@@ -275,12 +277,25 @@ void dinit_cap(struct ccx_demuxer *ctx)
|
||||
// The pointer may have been NULLed by dinit_libraries if it was shared
|
||||
if (iter->codec_private_data)
|
||||
{
|
||||
void *saved_private_data = iter->codec_private_data;
|
||||
if (iter->codec == CCX_CODEC_DVB)
|
||||
dvbsub_close_decoder(&iter->codec_private_data);
|
||||
else if (iter->codec == CCX_CODEC_TELETEXT)
|
||||
telxcc_close(&iter->codec_private_data, NULL);
|
||||
else
|
||||
free(iter->codec_private_data);
|
||||
|
||||
// Also NULL out any decoder contexts that shared this private_data pointer
|
||||
// to prevent use-after-free when PAT changes during stream processing
|
||||
if (lctx)
|
||||
{
|
||||
struct lib_cc_decode *dec_ctx;
|
||||
list_for_each_entry(dec_ctx, &lctx->dec_ctx_head, list, struct lib_cc_decode)
|
||||
{
|
||||
if (dec_ctx->private_data == saved_private_data)
|
||||
dec_ctx->private_data = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(iter);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user