diff --git a/libavformat/utils.c b/libavformat/utils.c index d113a16c80..2e50c5c0ee 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -5100,18 +5100,19 @@ AVRational av_guess_frame_rate(AVFormatContext *format, AVStream *st, AVFrame *f /** * Matches a stream specifier (but ignores requested index). * - * @param index set if a specific index is requested from the matching streams + * @param indexptr set to point to the requested stream index if there is one * * @return <0 on error * 0 if st is NOT a matching stream * >0 if st is a matching stream */ static int match_stream_specifier(AVFormatContext *s, AVStream *st, - const char *spec, int *index) + const char *spec, const char **indexptr) { + while (*spec) { if (*spec <= '9' && *spec >= '0') { /* opt:index */ - if (index) - *index = strtol(spec, NULL, 0); + if (indexptr) + *indexptr = spec; return 1; } else if (*spec == 'v' || *spec == 'a' || *spec == 's' || *spec == 'd' || *spec == 't' || *spec == 'V') { /* opt:[vasdtV] */ @@ -5127,6 +5128,9 @@ static int match_stream_specifier(AVFormatContext *s, AVStream *st, case 'V': type = AVMEDIA_TYPE_VIDEO; nopic = 1; break; default: av_assert0(0); } + if (*spec && *spec++ != ':') /* If we are not at the end, then another specifier must follow. */ + return AVERROR(EINVAL); + #if FF_API_LAVF_AVCTX FF_DISABLE_DEPRECATION_WARNINGS if (type != st->codecpar->codec_type @@ -5139,37 +5143,39 @@ FF_ENABLE_DEPRECATION_WARNINGS #endif if (nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC)) return 0; - if (*spec++ == ':') /* possibly followed by another specifier */ - return match_stream_specifier(s, st, spec, index); - return 1; } else if (*spec == 'p' && *(spec + 1) == ':') { int prog_id, i, j; + int found = 0; char *endptr; spec += 2; prog_id = strtol(spec, &endptr, 0); + /* Disallow empty id and make sure that if we are not at the end, then another specifier must follow. */ + if (spec == endptr || (*endptr && *endptr++ != ':')) + return AVERROR(EINVAL); + spec = endptr; for (i = 0; i < s->nb_programs; i++) { if (s->programs[i]->id != prog_id) continue; for (j = 0; j < s->programs[i]->nb_stream_indexes; j++) { if (st->index == s->programs[i]->stream_index[j]) { - if (*endptr++ == ':') { // p::.... - return match_stream_specifier(s, st, endptr, index); - } else { - return 1; - } + found = 1; + i = s->nb_programs; + break; } } } - return 0; + if (!found) + return 0; } else if (*spec == '#' || (*spec == 'i' && *(spec + 1) == ':')) { int stream_id; char *endptr; spec += 1 + (*spec == 'i'); stream_id = strtol(spec, &endptr, 0); - if (!*endptr) - return stream_id == st->id; + if (spec == endptr || *endptr) /* Disallow empty id and make sure we are at the end. */ + return AVERROR(EINVAL); + return stream_id == st->id; } else if (*spec == 'm' && *(spec + 1) == ':') { AVDictionaryEntry *tag; char *key, *val; @@ -5193,7 +5199,7 @@ FF_ENABLE_DEPRECATION_WARNINGS av_freep(&key); return ret; - } else if (*spec == 'u') { + } else if (*spec == 'u' && *(spec + 1) == '\0') { AVCodecParameters *par = st->codecpar; #if FF_API_LAVF_AVCTX FF_DISABLE_DEPRECATION_WARNINGS @@ -5238,40 +5244,54 @@ FF_ENABLE_DEPRECATION_WARNINGS #else return par->codec_id != AV_CODEC_ID_NONE && val != 0; #endif - } else if (!*spec) /* empty specifier, matches everything */ - return 1; + } else { + return AVERROR(EINVAL); + } + } - return AVERROR(EINVAL); + /* empty specifier, matches everything */ + return 1; } int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec) { - int ret; - int index = -1; + int ret, index; + char *endptr; + const char *indexptr = NULL; - /* This is not really needed but saves us a loop for simple stream index specifiers. */ - if (*spec <= '9' && *spec >= '0') /* opt:index */ - return strtol(spec, NULL, 0) == st->index; + ret = match_stream_specifier(s, st, spec, &indexptr); + if (ret < 0) + goto error; - ret = match_stream_specifier(s, st, spec, &index); - if (ret < 0) { - av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec); + if (!indexptr) return ret; + + index = strtol(indexptr, &endptr, 0); + if (*endptr) { /* We can't have anything after the requested index. */ + ret = AVERROR(EINVAL); + goto error; } - if (!ret || index < 0) - return ret; + + /* This is not really needed but saves us a loop for simple stream index specifiers. */ + if (spec == indexptr) + return (index == st->index); /* If we requested a matching stream index, we have to ensure st is that. */ for (int i = 0; i < s->nb_streams && index >= 0; i++) { - int ret = match_stream_specifier(s, s->streams[i], spec, NULL); + ret = match_stream_specifier(s, s->streams[i], spec, NULL); if (ret < 0) - return ret; + goto error; if (ret > 0 && index-- == 0 && st == s->streams[i]) return 1; } return 0; + +error: + if (ret == AVERROR(EINVAL)) + av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec); + return ret; } int ff_generate_avci_extradata(AVStream *st)