# UNDF: UNDF-2026-000000070 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -131,22 +131,108 @@ /**********************************************************/ +/* + * CWE-407 fix: replace O(N) linear codec tag array scans with O(1) hash-map + * lookups. Hash tables are built lazily on first use for each AVCodecTag + * array pointer (keyed on the array address). This avoids per-stream linear + * scans in hot muxer paths (movenc, matroskaenc, flvenc, cafenc, etc.). + * + * Implementation uses a simple open-addressing hash map over AVCodecID (int) + * and uint32_t (tag), both of which fit in a pointer-sized value. + */ + +#include "libavutil/mem.h" +#include + +#define CODEC_TAG_HASH_BITS 10 +#define CODEC_TAG_HASH_SIZE (1 << CODEC_TAG_HASH_BITS) +#define CODEC_TAG_HASH_MASK (CODEC_TAG_HASH_SIZE - 1) + +typedef struct { + enum AVCodecID id; + unsigned int tag; +} CodecTagEntry; + +typedef struct CodecTagIndex { + const AVCodecTag *src; /* pointer to the source array */ + CodecTagEntry *id2tag; /* hash: codec_id -> tag */ + CodecTagEntry *tag2id; /* hash: tag -> codec_id */ + struct CodecTagIndex *next; +} CodecTagIndex; + +static CodecTagIndex *codec_tag_index_list = NULL; +static pthread_mutex_t codec_tag_index_lock = PTHREAD_MUTEX_INITIALIZER; + +static CodecTagIndex *codec_tag_index_build(const AVCodecTag *tags) +{ + CodecTagIndex *idx = av_mallocz(sizeof(*idx)); + if (!idx) return NULL; + idx->src = tags; + idx->id2tag = av_calloc(CODEC_TAG_HASH_SIZE, sizeof(CodecTagEntry)); + idx->tag2id = av_calloc(CODEC_TAG_HASH_SIZE, sizeof(CodecTagEntry)); + if (!idx->id2tag || !idx->tag2id) { + av_free(idx->id2tag); av_free(idx->tag2id); av_free(idx); + return NULL; + } + for (const AVCodecTag *t = tags; t->id != AV_CODEC_ID_NONE; t++) { + /* id -> tag: open addressing, probe on collision */ + unsigned h = ((unsigned)t->id * 2654435761u) & CODEC_TAG_HASH_MASK; + while (idx->id2tag[h].id != AV_CODEC_ID_NONE && + idx->id2tag[h].id != t->id) + h = (h + 1) & CODEC_TAG_HASH_MASK; + if (idx->id2tag[h].id == AV_CODEC_ID_NONE) { + idx->id2tag[h].id = t->id; + idx->id2tag[h].tag = t->tag; + } + /* tag -> id: first match wins (mirrors original scan-order semantics) */ + unsigned g = (t->tag * 2246822519u) & CODEC_TAG_HASH_MASK; + while (idx->tag2id[g].id != AV_CODEC_ID_NONE) + g = (g + 1) & CODEC_TAG_HASH_MASK; + idx->tag2id[g].id = t->id; + idx->tag2id[g].tag = t->tag; + } + return idx; +} + +static CodecTagIndex *codec_tag_get_index(const AVCodecTag *tags) +{ + pthread_mutex_lock(&codec_tag_index_lock); + for (CodecTagIndex *idx = codec_tag_index_list; idx; idx = idx->next) + if (idx->src == tags) { pthread_mutex_unlock(&codec_tag_index_lock); return idx; } + CodecTagIndex *idx = codec_tag_index_build(tags); + if (idx) { idx->next = codec_tag_index_list; codec_tag_index_list = idx; } + pthread_mutex_unlock(&codec_tag_index_lock); + return idx; +} + unsigned int ff_codec_get_tag(const AVCodecTag *tags, enum AVCodecID id) { - while (tags->id != AV_CODEC_ID_NONE) { - if (tags->id == id) - return tags->tag; - tags++; + CodecTagIndex *idx = codec_tag_get_index(tags); + if (idx) { + unsigned h = ((unsigned)id * 2654435761u) & CODEC_TAG_HASH_MASK; + while (idx->id2tag[h].id != AV_CODEC_ID_NONE) { + if (idx->id2tag[h].id == id) return idx->id2tag[h].tag; + h = (h + 1) & CODEC_TAG_HASH_MASK; + } + return 0; } - return 0; + /* fallback: original linear scan if index alloc failed */ + while (tags->id != AV_CODEC_ID_NONE) { + if (tags->id == id) return tags->tag; + tags++; + } + return 0; /* FALLBACK */ } enum AVCodecID ff_codec_get_id(const AVCodecTag *tags, unsigned int tag) { - for (int i = 0; tags[i].id != AV_CODEC_ID_NONE; i++) - if (tag == tags[i].tag) - return tags[i].id; - for (int i = 0; tags[i].id != AV_CODEC_ID_NONE; i++) - if (ff_toupper4(tag) == ff_toupper4(tags[i].tag)) - return tags[i].id; + /* O(1) exact match via hash */ + CodecTagIndex *idx = codec_tag_get_index(tags); + if (idx) { + unsigned g = (tag * 2246822519u) & CODEC_TAG_HASH_MASK; + while (idx->tag2id[g].id != AV_CODEC_ID_NONE) { + if (idx->tag2id[g].tag == tag) return idx->tag2id[g].id; + g = (g + 1) & CODEC_TAG_HASH_MASK; + } + /* case-insensitive fallback: rare, small scan acceptable */ + for (int i = 0; tags[i].id != AV_CODEC_ID_NONE; i++) + if (ff_toupper4(tag) == ff_toupper4(tags[i].tag)) + return tags[i].id; + return AV_CODEC_ID_NONE; + } + /* fallback */ + for (int i = 0; tags[i].id != AV_CODEC_ID_NONE; i++) + if (tag == tags[i].tag) return tags[i].id; + for (int i = 0; tags[i].id != AV_CODEC_ID_NONE; i++) + if (ff_toupper4(tag) == ff_toupper4(tags[i].tag)) return tags[i].id; return AV_CODEC_ID_NONE; }