X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fid3v2.c;h=96226193c1dd74cd3cc7cb41acc87f3d47b89441;hb=f4df5039a791a56de85c64e6b9e4448a221b5c40;hp=b43ab1745f27dae3636eff5fb2592d9eea687240;hpb=94346ab593f3fc0822f11d3e3f378f9944ad2d40;p=ffmpeg diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c index b43ab1745f2..96226193c1d 100644 --- a/libavformat/id3v2.c +++ b/libavformat/id3v2.c @@ -225,7 +225,6 @@ static void free_geobtag(void *obj) av_freep(&geob->file_name); av_freep(&geob->description); av_freep(&geob->data); - av_free(geob); } /** @@ -361,8 +360,8 @@ static void read_uslt(AVFormatContext *s, AVIOContext *pb, int taglen, { uint8_t lang[4]; uint8_t *descriptor = NULL; // 'Content descriptor' - uint8_t *text = NULL; - char *key = NULL; + uint8_t *text; + char *key; int encoding; int ok = 0; @@ -387,18 +386,19 @@ static void read_uslt(AVFormatContext *s, AVIOContext *pb, int taglen, key = av_asprintf("lyrics-%s%s%s", descriptor[0] ? (char *)descriptor : "", descriptor[0] ? "-" : "", lang); - if (!key) + if (!key) { + av_free(text); goto error; + } - av_dict_set(metadata, key, text, 0); + av_dict_set(metadata, key, text, + AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); ok = 1; error: if (!ok) av_log(s, AV_LOG_ERROR, "Error reading lyrics, skipped\n"); av_free(descriptor); - av_free(text); - av_free(key); } /** @@ -443,12 +443,24 @@ static void read_comment(AVFormatContext *s, AVIOContext *pb, int taglen, av_dict_set(metadata, key, (const char *) dst, dict_flags); } +typedef struct ExtraMetaList { + ID3v2ExtraMeta *head, *tail; +} ExtraMetaList; + +static void list_append(ID3v2ExtraMeta *new_elem, ExtraMetaList *list) +{ + if (list->tail) + list->tail->next = new_elem; + else + list->head = new_elem; + list->tail = new_elem; +} + /** * Parse GEOB tag into a ID3v2ExtraMetaGEOB struct. */ static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen, - const char *tag, ID3v2ExtraMeta **extra_meta, - int isv34) + const char *tag, ExtraMetaList *extra_meta, int isv34) { ID3v2ExtraMetaGEOB *geob_data = NULL; ID3v2ExtraMeta *new_extra = NULL; @@ -458,20 +470,15 @@ static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen, if (taglen < 1) return; - geob_data = av_mallocz(sizeof(ID3v2ExtraMetaGEOB)); - if (!geob_data) { - av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n", - sizeof(ID3v2ExtraMetaGEOB)); - return; - } - new_extra = av_mallocz(sizeof(ID3v2ExtraMeta)); if (!new_extra) { av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n", sizeof(ID3v2ExtraMeta)); - goto fail; + return; } + geob_data = &new_extra->data.geob; + /* read encoding type byte */ encoding = avio_r8(pb); taglen--; @@ -510,9 +517,7 @@ static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen, /* add data to the list */ new_extra->tag = "GEOB"; - new_extra->data = geob_data; - new_extra->next = *extra_meta; - *extra_meta = new_extra; + list_append(new_extra, extra_meta); return; @@ -576,7 +581,6 @@ static void free_apic(void *obj) ID3v2ExtraMetaAPIC *apic = obj; av_buffer_unref(&apic->buf); av_freep(&apic->description); - av_freep(&apic); } static void rstrip_spaces(char *buf) @@ -587,8 +591,7 @@ static void rstrip_spaces(char *buf) } static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen, - const char *tag, ID3v2ExtraMeta **extra_meta, - int isv34) + const char *tag, ExtraMetaList *extra_meta, int isv34) { int enc, pic_type; char mimetype[64] = {0}; @@ -602,16 +605,20 @@ static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen, goto fail; new_extra = av_mallocz(sizeof(*new_extra)); - apic = av_mallocz(sizeof(*apic)); - if (!new_extra || !apic) + if (!new_extra) goto fail; + apic = &new_extra->data.apic; + enc = avio_r8(pb); taglen--; /* mimetype */ if (isv34) { - taglen -= avio_get_str(pb, taglen, mimetype, sizeof(mimetype)); + int ret = avio_get_str(pb, taglen, mimetype, sizeof(mimetype)); + if (ret < 0 || ret >= taglen) + goto fail; + taglen -= ret; } else { if (avio_read(pb, mimetype, 3) < 0) goto fail; @@ -657,13 +664,11 @@ static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen, memset(apic->buf->data + taglen, 0, AV_INPUT_BUFFER_PADDING_SIZE); new_extra->tag = "APIC"; - new_extra->data = apic; - new_extra->next = *extra_meta; - *extra_meta = new_extra; // The description must be unique, and some ID3v2 tag writers add spaces // to write several APIC entries with the same description. rstrip_spaces(apic->description); + list_append(new_extra, extra_meta); return; @@ -679,10 +684,10 @@ static void free_chapter(void *obj) ID3v2ExtraMetaCHAP *chap = obj; av_freep(&chap->element_id); av_dict_free(&chap->meta); - av_freep(&chap); } -static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, const char *ttag, ID3v2ExtraMeta **extra_meta, int isv34) +static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, + const char *ttag, ExtraMetaList *extra_meta, int isv34) { int taglen; char tag[5]; @@ -690,10 +695,10 @@ static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, const cha ID3v2ExtraMetaCHAP *chap = NULL; new_extra = av_mallocz(sizeof(*new_extra)); - chap = av_mallocz(sizeof(*chap)); + if (!new_extra) + return; - if (!new_extra || !chap) - goto fail; + chap = &new_extra->data.chap; if (decode_str(s, pb, 0, &chap->element_id, &len) < 0) goto fail; @@ -726,15 +731,12 @@ static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, const cha ff_metadata_conv(&chap->meta, NULL, ff_id3v2_4_metadata_conv); new_extra->tag = "CHAP"; - new_extra->data = chap; - new_extra->next = *extra_meta; - *extra_meta = new_extra; + list_append(new_extra, extra_meta); return; fail: - if (chap) - free_chapter(chap); + free_chapter(chap); av_freep(&new_extra); } @@ -743,20 +745,19 @@ static void free_priv(void *obj) ID3v2ExtraMetaPRIV *priv = obj; av_freep(&priv->owner); av_freep(&priv->data); - av_freep(&priv); } static void read_priv(AVFormatContext *s, AVIOContext *pb, int taglen, - const char *tag, ID3v2ExtraMeta **extra_meta, int isv34) + const char *tag, ExtraMetaList *extra_meta, int isv34) { ID3v2ExtraMeta *meta; ID3v2ExtraMetaPRIV *priv; meta = av_mallocz(sizeof(*meta)); - priv = av_mallocz(sizeof(*priv)); + if (!meta) + return; - if (!meta || !priv) - goto fail; + priv = &meta->data.priv; if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &priv->owner, &taglen) < 0) goto fail; @@ -771,15 +772,12 @@ static void read_priv(AVFormatContext *s, AVIOContext *pb, int taglen, goto fail; meta->tag = "PRIV"; - meta->data = priv; - meta->next = *extra_meta; - *extra_meta = meta; + list_append(meta, extra_meta); return; fail: - if (priv) - free_priv(priv); + free_priv(priv); av_freep(&meta); } @@ -787,7 +785,7 @@ typedef struct ID3v2EMFunc { const char *tag3; const char *tag4; void (*read)(AVFormatContext *s, AVIOContext *pb, int taglen, - const char *tag, ID3v2ExtraMeta **extra_meta, + const char *tag, ExtraMetaList *extra_meta, int isv34); void (*free)(void *obj); } ID3v2EMFunc; @@ -821,12 +819,12 @@ static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34) static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata, AVFormatContext *s, int len, uint8_t version, - uint8_t flags, ID3v2ExtraMeta **extra_meta) + uint8_t flags, ExtraMetaList *extra_meta) { int isv34, unsync; unsigned tlen; char tag[5]; - int64_t next, end = avio_tell(pb) + len; + int64_t next, end = avio_tell(pb); int taghdrlen; const char *reason = NULL; AVIOContext pb_local; @@ -838,6 +836,10 @@ static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata, av_unused int uncompressed_buffer_size = 0; const char *comm_frame; + if (end > INT64_MAX - len - 10) + return; + end += len; + av_log(s, AV_LOG_DEBUG, "id3v2 ver:%d flags:%02X len:%d\n", version, flags, len); switch (version) { @@ -1006,6 +1008,11 @@ static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata, av_log(s, AV_LOG_DEBUG, "Compresssed frame %s tlen=%d dlen=%ld\n", tag, tlen, dlen); + if (tlen <= 0) + goto seek; + if (dlen / 32768 > tlen) + goto seek; + av_fast_malloc(&uncompressed_buffer, &uncompressed_buffer_size, dlen); if (!uncompressed_buffer) { av_log(s, AV_LOG_ERROR, "Failed to alloc %ld bytes\n", dlen); @@ -1068,13 +1075,17 @@ error: static void id3v2_read_internal(AVIOContext *pb, AVDictionary **metadata, AVFormatContext *s, const char *magic, - ID3v2ExtraMeta **extra_meta, int64_t max_search_size) + ID3v2ExtraMeta **extra_metap, int64_t max_search_size) { int len, ret; uint8_t buf[ID3v2_HEADER_SIZE]; + ExtraMetaList extra_meta = { NULL }; int found_header; int64_t start, off; + if (extra_metap) + *extra_metap = NULL; + if (max_search_size && max_search_size < ID3v2_HEADER_SIZE) return; @@ -1101,7 +1112,8 @@ static void id3v2_read_internal(AVIOContext *pb, AVDictionary **metadata, ((buf[7] & 0x7f) << 14) | ((buf[8] & 0x7f) << 7) | (buf[9] & 0x7f); - id3v2_parse(pb, metadata, s, len, buf[3], buf[5], extra_meta); + id3v2_parse(pb, metadata, s, len, buf[3], buf[5], + extra_metap ? &extra_meta : NULL); } else { avio_seek(pb, off, SEEK_SET); } @@ -1110,6 +1122,8 @@ static void id3v2_read_internal(AVIOContext *pb, AVDictionary **metadata, ff_metadata_conv(metadata, NULL, id3v2_2_metadata_conv); ff_metadata_conv(metadata, NULL, ff_id3v2_4_metadata_conv); merge_date(metadata); + if (extra_metap) + *extra_metap = extra_meta.head; } void ff_id3v2_read_dict(AVIOContext *pb, AVDictionary **metadata, @@ -1131,7 +1145,7 @@ void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta) while (current) { if ((extra_func = get_extra_meta_func(current->tag, 1))) - extra_func->free(current->data); + extra_func->free(¤t->data); next = current->next; av_freep(¤t); current = next; @@ -1140,105 +1154,70 @@ void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta) *extra_meta = NULL; } -int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta) +int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta *extra_meta) { ID3v2ExtraMeta *cur; - for (cur = *extra_meta; cur; cur = cur->next) { + for (cur = extra_meta; cur; cur = cur->next) { ID3v2ExtraMetaAPIC *apic; AVStream *st; + int ret; if (strcmp(cur->tag, "APIC")) continue; - apic = cur->data; + apic = &cur->data.apic; - if (!(st = avformat_new_stream(s, NULL))) - return AVERROR(ENOMEM); - - st->disposition |= AV_DISPOSITION_ATTACHED_PIC; - st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + ret = ff_add_attached_pic(s, NULL, NULL, &apic->buf, 0); + if (ret < 0) + return ret; + st = s->streams[s->nb_streams - 1]; st->codecpar->codec_id = apic->id; - if (AV_RB64(apic->buf->data) == PNGSIG) + if (AV_RB64(st->attached_pic.data) == PNGSIG) st->codecpar->codec_id = AV_CODEC_ID_PNG; if (apic->description[0]) av_dict_set(&st->metadata, "title", apic->description, 0); av_dict_set(&st->metadata, "comment", apic->type, 0); - - av_init_packet(&st->attached_pic); - st->attached_pic.buf = apic->buf; - st->attached_pic.data = apic->buf->data; - st->attached_pic.size = apic->buf->size - AV_INPUT_BUFFER_PADDING_SIZE; - st->attached_pic.stream_index = st->index; - st->attached_pic.flags |= AV_PKT_FLAG_KEY; - - apic->buf = NULL; } return 0; } -int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta **extra_meta) +int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta *cur) { - int ret = 0; - ID3v2ExtraMeta *cur; AVRational time_base = {1, 1000}; - ID3v2ExtraMetaCHAP **chapters = NULL; - int num_chapters = 0; - int i; + int ret; - // since extra_meta is a linked list where elements are prepended, - // we need to reverse the order of chapters - for (cur = *extra_meta; cur; cur = cur->next) { + for (unsigned i = 0; cur; cur = cur->next) { ID3v2ExtraMetaCHAP *chap; + AVChapter *chapter; if (strcmp(cur->tag, "CHAP")) continue; - chap = cur->data; - - if ((ret = av_dynarray_add_nofree(&chapters, &num_chapters, chap)) < 0) - goto end; - } - - for (i = 0; i < (num_chapters / 2); i++) { - ID3v2ExtraMetaCHAP *right; - int right_index; - - right_index = (num_chapters - 1) - i; - right = chapters[right_index]; - - chapters[right_index] = chapters[i]; - chapters[i] = right; - } - for (i = 0; i < num_chapters; i++) { - ID3v2ExtraMetaCHAP *chap; - AVChapter *chapter; - - chap = chapters[i]; - chapter = avpriv_new_chapter(s, i, time_base, chap->start, chap->end, chap->element_id); + chap = &cur->data.chap; + chapter = avpriv_new_chapter(s, i++, time_base, chap->start, + chap->end, chap->element_id); if (!chapter) continue; if ((ret = av_dict_copy(&chapter->metadata, chap->meta, 0)) < 0) - goto end; + return ret; } -end: - av_freep(&chapters); - return ret; + return 0; } -int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta **extra_meta) +int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta *extra_meta) { ID3v2ExtraMeta *cur; int dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL; - for (cur = *extra_meta; cur; cur = cur->next) { + for (cur = extra_meta; cur; cur = cur->next) { if (!strcmp(cur->tag, "PRIV")) { - ID3v2ExtraMetaPRIV *priv = cur->data; + ID3v2ExtraMetaPRIV *priv = &cur->data.priv; AVBPrint bprint; char *escaped, *key; int i, ret; @@ -1263,8 +1242,6 @@ int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta **extra_met } if ((ret = av_dict_set(metadata, key, escaped, dict_flags)) < 0) { - av_free(key); - av_free(escaped); return ret; } } @@ -1273,7 +1250,7 @@ int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta **extra_met return 0; } -int ff_id3v2_parse_priv(AVFormatContext *s, ID3v2ExtraMeta **extra_meta) +int ff_id3v2_parse_priv(AVFormatContext *s, ID3v2ExtraMeta *extra_meta) { return ff_id3v2_parse_priv_dict(&s->metadata, extra_meta); }