From 5dc8292dd22c5fc523b2d11a69990ab8e4b23112 Mon Sep 17 00:00:00 2001 From: Amrit kumar Mahto Date: Fri, 2 Jan 2026 16:58:09 +0530 Subject: [PATCH 1/7] Fix out-of-bounds read in H.264 SEI parsing --- src/lib_ccx/avc_functions.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lib_ccx/avc_functions.c b/src/lib_ccx/avc_functions.c index 0b377bf5e..0e83831f4 100644 --- a/src/lib_ccx/avc_functions.c +++ b/src/lib_ccx/avc_functions.c @@ -369,6 +369,8 @@ void sei_rbsp(struct avc_ctx *ctx, unsigned char *seibuf, unsigned char *seiend) while (tbuf < seiend - 1) // Use -1 because of trailing marker { tbuf = sei_message(ctx, tbuf, seiend - 1); + if (!tbuf) + break; } if (tbuf == seiend - 1) { @@ -392,20 +394,24 @@ void sei_rbsp(struct avc_ctx *ctx, unsigned char *seibuf, unsigned char *seiend) unsigned char *sei_message(struct avc_ctx *ctx, unsigned char *seibuf, unsigned char *seiend) { int payload_type = 0; - while (*seibuf == 0xff) + while (seibuf < seiend && *seibuf == 0xff) { payload_type += 255; seibuf++; } + if (seibuf >= seiend) + return NULL; payload_type += *seibuf; seibuf++; int payload_size = 0; - while (*seibuf == 0xff) + while (seibuf < seiend && *seibuf == 0xff) { payload_size += 255; seibuf++; } + if (seibuf >= seiend) + return NULL; payload_size += *seibuf; seibuf++; From 82109e6cd9c16c558868de8f75f299eade0cad70 Mon Sep 17 00:00:00 2001 From: Amrit kumar Mahto Date: Fri, 2 Jan 2026 17:27:15 +0530 Subject: [PATCH 2/7] Fix DTVCC structural type confusion and OOB writes (#1961) --- src/lib_ccx/ccx_decoders_708.c | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/lib_ccx/ccx_decoders_708.c b/src/lib_ccx/ccx_decoders_708.c index eda2a9b8a..0fc885059 100644 --- a/src/lib_ccx/ccx_decoders_708.c +++ b/src/lib_ccx/ccx_decoders_708.c @@ -582,9 +582,23 @@ void dtvcc_window_copy_to_screen(dtvcc_service_decoder *decoder, dtvcc_window *w top = top < 0 ? 0 : top; left = left < 0 ? 0 : left; + // Invariant check: window should never be defined with values exceeding MAX constants + if (window->row_count > CCX_DTVCC_MAX_ROWS || window->col_count > CCX_DTVCC_MAX_COLUMNS) + { + ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_window_copy_to_screen: W-%d HAS INVALID DIMENSIONS %dx%d\n", + window->number, window->row_count, window->col_count); + return; + } + int copyrows = top + window->row_count >= CCX_DTVCC_SCREENGRID_ROWS ? CCX_DTVCC_SCREENGRID_ROWS - top : window->row_count; int copycols = left + window->col_count >= CCX_DTVCC_SCREENGRID_COLUMNS ? CCX_DTVCC_SCREENGRID_COLUMNS - left : window->col_count; + // Use window dimensions as secondary safety guard + if (copyrows > window->row_count) + copyrows = window->row_count; + if (copycols > window->col_count) + copycols = window->col_count; + ccx_common_logging.debug_ftn( CCX_DMT_708, "[CEA-708] %d*%d will be copied to the TV.\n", copyrows, copycols); @@ -795,6 +809,14 @@ void dtvcc_process_character(dtvcc_service_decoder *decoder, dtvcc_symbol symbol if (cw == -1 || !window->is_defined) // Writing to a non existing window, skipping return; + if (window->pen_row < 0 || window->pen_row >= CCX_DTVCC_MAX_ROWS || + window->pen_column < 0 || window->pen_column >= CCX_DTVCC_MAX_COLUMNS) + { + ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_character: pen out of bounds (%d:%d)\n", + window->pen_row, window->pen_column); + return; + } + window->is_empty = 0; window->rows[window->pen_row][window->pen_column] = symbol; window->pen_attribs[window->pen_row][window->pen_column] = window->pen_attribs_pattern; // "Painting" char by pen - attribs @@ -998,6 +1020,14 @@ void dtvcc_handle_DFx_DefineWindow(dtvcc_service_decoder *decoder, int window_id int row_count = (data[4] & 0xf) + 1; // according to CEA-708-D int anchor_point = data[4] >> 4; int col_count = (data[5] & 0x3f) + 1; // according to CEA-708-D + + if (row_count > CCX_DTVCC_MAX_ROWS || col_count > CCX_DTVCC_MAX_COLUMNS) + { + ccx_common_logging.log_ftn("[CEA-708] Invalid window size %dx%d (max %dx%d), rejecting window definition\n", + row_count, col_count, CCX_DTVCC_MAX_ROWS, CCX_DTVCC_MAX_COLUMNS); + return; + } + int pen_style = data[6] & 0x7; int win_style = (data[6] >> 3) & 0x7; @@ -1341,6 +1371,14 @@ void dtvcc_handle_SPL_SetPenLocation(dtvcc_service_decoder *decoder, unsigned ch } dtvcc_window *window = &decoder->windows[decoder->current_window]; + if (row >= window->row_count || col >= window->col_count) + { + ccx_common_logging.log_ftn("[CEA-708] dtvcc_handle_SPL_SetPenLocation: " + "Invalid pen location %d:%d for window size %dx%d, rejecting command\n", + row, col, window->row_count, window->col_count); + return; + } + window->pen_row = row; window->pen_column = col; } From 3e1424cda80b7b6f863aed0c27eb749d76981e78 Mon Sep 17 00:00:00 2001 From: Amrit kumar Mahto Date: Fri, 2 Jan 2026 17:52:25 +0530 Subject: [PATCH 3/7] Fix TS/ES: Integer overflow, stack overflow, heap over-read --- src/lib_ccx/es_userdata.c | 8 ++++++-- src/lib_ccx/ts_tables.c | 9 +++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/lib_ccx/es_userdata.c b/src/lib_ccx/es_userdata.c index 7e4cac8a9..87e1cfcef 100644 --- a/src/lib_ccx/es_userdata.c +++ b/src/lib_ccx/es_userdata.c @@ -142,7 +142,7 @@ int user_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, struct { if ((ud_header[1] & 0x7F) == 0x01) { - unsigned char cc_data[3 * 31 + 1]; // Maximum cc_count is 31 + unsigned char cc_data[3 * 32]; // Increased for safety margin, 31 is max count dec_ctx->stat_scte20ccheaders++; read_bytes(ustream, 2); // "03 01" @@ -370,6 +370,7 @@ int user_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, struct dbg_print(CCX_DMT_PARSE, "%s", debug_608_to_ASC(dishdata, 0)); dbg_print(CCX_DMT_PARSE, "%s:\n", debug_608_to_ASC(dishdata + 3, 0)); + dishdata[cc_count * 3] = 0xFF; // Ensure termination for store_hdcc store_hdcc(enc_ctx, dec_ctx, dishdata, cc_count, dec_ctx->timing->current_tref, dec_ctx->timing->fts_now, sub); // Ignore 4 (0x020A, followed by two unknown) bytes. @@ -484,7 +485,10 @@ int user_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, struct mprint("MPEG:VBI: only support Luma line\n"); if (udatalen < 720) - mprint("MPEG:VBI: Minimum 720 bytes in luma line required\n"); + { + mprint("MPEG:VBI: Minimum 720 bytes in luma line required, skipping truncated packet.\n"); + return 1; + } decode_vbi(dec_ctx, field, ustream->pos, 720, sub); dbg_print(CCX_DMT_VERBOSE, "GXF (vbi line %d) user data:\n", line_nb); diff --git a/src/lib_ccx/ts_tables.c b/src/lib_ccx/ts_tables.c index 87630b5e4..89611907c 100644 --- a/src/lib_ccx/ts_tables.c +++ b/src/lib_ccx/ts_tables.c @@ -574,6 +574,15 @@ void ts_buffer_psi_packet(struct ccx_demuxer *ctx) else if (ccounter == ctx->PID_buffers[pid]->prev_ccounter + 1 || (ctx->PID_buffers[pid]->prev_ccounter == 0x0f && ccounter == 0)) { ctx->PID_buffers[pid]->prev_ccounter = ccounter; + // Check for integer overflow and reasonable size limit (1MB) + if (ctx->PID_buffers[pid]->buffer_length > 1024 * 1024 || + payload_length > 1024 * 1024 || + ctx->PID_buffers[pid]->buffer_length + payload_length > 1024 * 1024) + { + dbg_print(CCX_DMT_GENERIC_NOTICES, "\rWarning: PSI buffer for PID %u exceeded reasonable limit (1MB), discarding.\n", pid); + return; + } + void *tmp = realloc(ctx->PID_buffers[pid]->buffer, ctx->PID_buffers[pid]->buffer_length + payload_length); if (tmp == NULL) { From cc7a43b5e2187d56caa26419cda20983ec34df43 Mon Sep 17 00:00:00 2001 From: Amrit kumar Mahto Date: Fri, 2 Jan 2026 18:09:15 +0530 Subject: [PATCH 4/7] [FIX] Teletext decoder: fix OOB read/write and loop overflow (#1965) --- src/lib_ccx/telxcc.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/lib_ccx/telxcc.c b/src/lib_ccx/telxcc.c index fb32d9dda..7204ac034 100644 --- a/src/lib_ccx/telxcc.c +++ b/src/lib_ccx/telxcc.c @@ -1392,7 +1392,7 @@ int tlt_process_pes_packet(struct lib_cc_decode *dec_ctx, uint8_t *buffer, uint1 uint8_t pes_ext_flag; // extension uint32_t t = 0; - uint16_t i; + uint32_t i; struct TeletextCtx *ctx = dec_ctx->private_data; ctx->sentence_cap = sentence_cap; @@ -1468,6 +1468,9 @@ int tlt_process_pes_packet(struct lib_cc_decode *dec_ctx, uint8_t *buffer, uint1 if (pes_packet_length > size) pes_packet_length = size; + if (size < 9) + return CCX_OK; + // optional PES header marker bits (10.. ....) if ((buffer[6] & 0xc0) == 0x80) { @@ -1480,8 +1483,16 @@ int tlt_process_pes_packet(struct lib_cc_decode *dec_ctx, uint8_t *buffer, uint1 { if ((optional_pes_header_included == YES) && ((buffer[7] & 0x80) > 0)) { - ctx->using_pts = YES; - dbg_print(CCX_DMT_TELETEXT, "- PID 0xbd PTS available\n"); + if (size < 14) + { + ctx->using_pts = NO; + dbg_print(CCX_DMT_TELETEXT, "- PID 0xbd PTS signaled but packet too short, using TS PCR\n"); + } + else + { + ctx->using_pts = YES; + dbg_print(CCX_DMT_TELETEXT, "- PID 0xbd PTS available\n"); + } } else { @@ -1554,11 +1565,17 @@ int tlt_process_pes_packet(struct lib_cc_decode *dec_ctx, uint8_t *buffer, uint1 if (optional_pes_header_included == YES) i += 3 + optional_pes_header_length; - while (i <= pes_packet_length - 6) + while (i + 2 <= pes_packet_length) { uint8_t data_unit_id = buffer[i++]; uint8_t data_unit_len = buffer[i++]; + if (i + data_unit_len > pes_packet_length) + { + dbg_print(CCX_DMT_TELETEXT, "- Teletext data unit length %u exceeds PES packet length, stopping.\n", data_unit_len); + break; + } + if ((data_unit_id == DATA_UNIT_EBU_TELETEXT_NONSUBTITLE) || (data_unit_id == DATA_UNIT_EBU_TELETEXT_SUBTITLE)) { // teletext payload has always size 44 bytes From 028ce9d0b59fc4f078b6d6754177648b3e2dc90f Mon Sep 17 00:00:00 2001 From: Amrit kumar Mahto Date: Fri, 2 Jan 2026 18:26:19 +0530 Subject: [PATCH 5/7] [FIX] DTVCC: Heap Overflow & OOB Read --- src/lib_ccx/ccx_decoders_708.c | 6 ++++++ src/lib_ccx/ccx_dtvcc.c | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lib_ccx/ccx_decoders_708.c b/src/lib_ccx/ccx_decoders_708.c index 0fc885059..ed9b88afd 100644 --- a/src/lib_ccx/ccx_decoders_708.c +++ b/src/lib_ccx/ccx_decoders_708.c @@ -1792,6 +1792,12 @@ void dtvcc_process_current_packet(dtvcc_ctx *dtvcc, int len) if (service_number == 7) // There is an extended header { + if (pos + 1 >= dtvcc->current_packet + len) + { + ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_current_packet: " + "Truncated extended header, stopping.\n"); + break; + } pos++; service_number = (pos[0] & 0x3F); // 6 more significant bits // printf ("Extended header: Service number: [%d]\n",service_number); diff --git a/src/lib_ccx/ccx_dtvcc.c b/src/lib_ccx/ccx_dtvcc.c index fa34e1379..be4e33536 100644 --- a/src/lib_ccx/ccx_dtvcc.c +++ b/src/lib_ccx/ccx_dtvcc.c @@ -25,7 +25,7 @@ void dtvcc_process_data(struct dtvcc_ctx *dtvcc, ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: DTVCC Channel Packet Data\n"); if (cc_valid && dtvcc->is_current_packet_header_parsed) { - if (dtvcc->current_packet_length > 253) + if (dtvcc->current_packet_length + 2 > CCX_DTVCC_MAX_PACKET_LENGTH) { ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: " "Warning: Legal packet size exceeded (1), data not added.\n"); @@ -51,7 +51,7 @@ void dtvcc_process_data(struct dtvcc_ctx *dtvcc, ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: DTVCC Channel Packet Start\n"); if (cc_valid) { - if (dtvcc->current_packet_length > CCX_DTVCC_MAX_PACKET_LENGTH - 1) + if (dtvcc->current_packet_length + 2 > CCX_DTVCC_MAX_PACKET_LENGTH) { ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: " "Warning: Legal packet size exceeded (2), data not added.\n"); From 1255b318aec913c05f4157175b429be8f1a33d5e Mon Sep 17 00:00:00 2001 From: Amrit kumar Mahto Date: Sat, 3 Jan 2026 03:06:23 +0530 Subject: [PATCH 6/7] [FIX] Remove dead safety checks per reviewer feedback --- src/lib_ccx/avc_functions.c | 2 -- src/lib_ccx/ccx_decoders_708.c | 22 +++------------------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/src/lib_ccx/avc_functions.c b/src/lib_ccx/avc_functions.c index 0e83831f4..f9349a42a 100644 --- a/src/lib_ccx/avc_functions.c +++ b/src/lib_ccx/avc_functions.c @@ -369,8 +369,6 @@ void sei_rbsp(struct avc_ctx *ctx, unsigned char *seibuf, unsigned char *seiend) while (tbuf < seiend - 1) // Use -1 because of trailing marker { tbuf = sei_message(ctx, tbuf, seiend - 1); - if (!tbuf) - break; } if (tbuf == seiend - 1) { diff --git a/src/lib_ccx/ccx_decoders_708.c b/src/lib_ccx/ccx_decoders_708.c index ed9b88afd..bc7e8f49d 100644 --- a/src/lib_ccx/ccx_decoders_708.c +++ b/src/lib_ccx/ccx_decoders_708.c @@ -582,22 +582,12 @@ void dtvcc_window_copy_to_screen(dtvcc_service_decoder *decoder, dtvcc_window *w top = top < 0 ? 0 : top; left = left < 0 ? 0 : left; - // Invariant check: window should never be defined with values exceeding MAX constants - if (window->row_count > CCX_DTVCC_MAX_ROWS || window->col_count > CCX_DTVCC_MAX_COLUMNS) - { - ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_window_copy_to_screen: W-%d HAS INVALID DIMENSIONS %dx%d\n", - window->number, window->row_count, window->col_count); - return; - } + int copyrows = top + window->row_count >= CCX_DTVCC_SCREENGRID_ROWS ? CCX_DTVCC_SCREENGRID_ROWS - top : window->row_count; int copycols = left + window->col_count >= CCX_DTVCC_SCREENGRID_COLUMNS ? CCX_DTVCC_SCREENGRID_COLUMNS - left : window->col_count; - // Use window dimensions as secondary safety guard - if (copyrows > window->row_count) - copyrows = window->row_count; - if (copycols > window->col_count) - copycols = window->col_count; + ccx_common_logging.debug_ftn( CCX_DMT_708, "[CEA-708] %d*%d will be copied to the TV.\n", copyrows, copycols); @@ -809,13 +799,7 @@ void dtvcc_process_character(dtvcc_service_decoder *decoder, dtvcc_symbol symbol if (cw == -1 || !window->is_defined) // Writing to a non existing window, skipping return; - if (window->pen_row < 0 || window->pen_row >= CCX_DTVCC_MAX_ROWS || - window->pen_column < 0 || window->pen_column >= CCX_DTVCC_MAX_COLUMNS) - { - ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_character: pen out of bounds (%d:%d)\n", - window->pen_row, window->pen_column); - return; - } + window->is_empty = 0; window->rows[window->pen_row][window->pen_column] = symbol; From 44eb665cd8a3e629a664b83be051a0d14e1f07af Mon Sep 17 00:00:00 2001 From: Amrit kumar Mahto Date: Sat, 3 Jan 2026 03:12:19 +0530 Subject: [PATCH 7/7] chore: apply clang-format fixes --- src/lib_ccx/ccx_decoders_708.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/lib_ccx/ccx_decoders_708.c b/src/lib_ccx/ccx_decoders_708.c index bc7e8f49d..86894f9b0 100644 --- a/src/lib_ccx/ccx_decoders_708.c +++ b/src/lib_ccx/ccx_decoders_708.c @@ -582,13 +582,9 @@ void dtvcc_window_copy_to_screen(dtvcc_service_decoder *decoder, dtvcc_window *w top = top < 0 ? 0 : top; left = left < 0 ? 0 : left; - - int copyrows = top + window->row_count >= CCX_DTVCC_SCREENGRID_ROWS ? CCX_DTVCC_SCREENGRID_ROWS - top : window->row_count; int copycols = left + window->col_count >= CCX_DTVCC_SCREENGRID_COLUMNS ? CCX_DTVCC_SCREENGRID_COLUMNS - left : window->col_count; - - ccx_common_logging.debug_ftn( CCX_DMT_708, "[CEA-708] %d*%d will be copied to the TV.\n", copyrows, copycols); @@ -799,8 +795,6 @@ void dtvcc_process_character(dtvcc_service_decoder *decoder, dtvcc_symbol symbol if (cw == -1 || !window->is_defined) // Writing to a non existing window, skipping return; - - window->is_empty = 0; window->rows[window->pen_row][window->pen_column] = symbol; window->pen_attribs[window->pen_row][window->pen_column] = window->pen_attribs_pattern; // "Painting" char by pen - attribs