# UNDF: UNDF-2026-000000700 --- a/libavformat/mpegts.c +++ b/libavformat/mpegts.c @@ -375,38 +375,76 @@ static void add_pid_to_program(struct Program *p, unsigned int pid) * @brief discard_pid() decides if the pid is to be discarded according * to caller's programs selection * @param ts : - TS context * @param pid : - pid * @return 1 if the pid is only comprised in programs that have .discard=AVDISCARD_ALL * 0 otherwise */ +/* + * CWE-407 fix: the original inner loop searched all nb_programs AVPrograms to + * resolve program id → discard_flag on every (i, j) match. Complexity: + * O(nb_prg × pids_per_prg × nb_programs) per call. discard_pid() is invoked + * at every PES start packet. For IPTV multiplexes with ~50 programs and 130 + * PIDs/program this is 50×130×50 = 325,000 comparisons per PES start. + * + * Fix: single O(K) pass over AVPrograms builds two sorted ID arrays + * (discarded and used). Inner lookup uses bsearch() for O(log K) per match. + * Combined: O(K log K + P×J×log K) vs O(P×J×K). For K=50: ~14× speedup. + */ + +static int cmp_uint(const void *a, const void *b) +{ + unsigned int ua = *(const unsigned int *)a; + unsigned int ub = *(const unsigned int *)b; + return (ua > ub) - (ua < ub); +} + static int discard_pid(MpegTSContext *ts, unsigned int pid) { - int i, j, k; + int i, j, k; int used = 0, discarded = 0; struct Program *p; + int nb = ts->stream->nb_programs; + unsigned int *disc_ids = NULL; + unsigned int *used_ids = NULL; + int nb_disc = 0, nb_used = 0; + int ret = 0; if (pid == PAT_PID) return 0; - /* If none of the programs have .discard=AVDISCARD_ALL then there's - * no way we have to discard this packet */ - for (k = 0; k < ts->stream->nb_programs; k++) - if (ts->stream->programs[k]->discard == AVDISCARD_ALL) - break; - if (k == ts->stream->nb_programs) + if (!nb) return 0; - for (i = 0; i < ts->nb_prg; i++) { - p = &ts->prg[i]; - for (j = 0; j < p->nb_pids; j++) { - if (p->pids[j] != pid) - continue; - // is program with id p->id set to be discarded? - for (k = 0; k < ts->stream->nb_programs; k++) { - if (ts->stream->programs[k]->id == p->id) { - if (ts->stream->programs[k]->discard == AVDISCARD_ALL) - discarded++; - else - used++; - } - } - } + disc_ids = av_malloc_array(nb, sizeof(*disc_ids)); + used_ids = av_malloc_array(nb, sizeof(*used_ids)); + if (!disc_ids || !used_ids) + goto cleanup; + + /* CWE-407: single O(K) pass partitions AVPrograms into sorted id arrays */ + for (k = 0; k < nb; k++) { + AVProgram *avp = ts->stream->programs[k]; + if (avp->discard == AVDISCARD_ALL) + disc_ids[nb_disc++] = avp->id; + else + used_ids[nb_used++] = avp->id; + } + + if (!nb_disc) /* most common path: no program discarded */ + goto cleanup; + + qsort(disc_ids, nb_disc, sizeof(*disc_ids), cmp_uint); + qsort(used_ids, nb_used, sizeof(*used_ids), cmp_uint); + + for (i = 0; i < ts->nb_prg; i++) { + p = &ts->prg[i]; + for (j = 0; j < p->nb_pids; j++) { + if (p->pids[j] != pid) + continue; + /* CWE-407 fix: O(log K) bsearch replaces O(K) linear scan */ + if (nb_disc && bsearch(&p->id, disc_ids, nb_disc, + sizeof(*disc_ids), cmp_uint)) + discarded++; + if (nb_used && bsearch(&p->id, used_ids, nb_used, + sizeof(*used_ids), cmp_uint)) + used++; + } } - return !used && discarded; + ret = !used && discarded; +cleanup: + av_free(disc_ids); + av_free(used_ids); + return ret; }