Commit 24e0e149 authored by Marton Balint's avatar Marton Balint

avformat/utils: be more strict about stream specifiers

This reworks the code to be more strict about accepting stream specifiers. From
now on we strictly enforce the syntax in the documentation up until the
decisive part of the stream specifier. Therefore matching stream specifiers
always need to be correct, non matching specifiers only need to be correct
until the decisive part.

Also recursion is changed to a simple loop.
Signed-off-by: 's avatarMarton Balint <cus@passwd.hu>
parent 837f2c97
...@@ -5100,18 +5100,19 @@ AVRational av_guess_frame_rate(AVFormatContext *format, AVStream *st, AVFrame *f ...@@ -5100,18 +5100,19 @@ AVRational av_guess_frame_rate(AVFormatContext *format, AVStream *st, AVFrame *f
/** /**
* Matches a stream specifier (but ignores requested index). * 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 * @return <0 on error
* 0 if st is NOT a matching stream * 0 if st is NOT a matching stream
* >0 if st is a matching stream * >0 if st is a matching stream
*/ */
static int match_stream_specifier(AVFormatContext *s, AVStream *st, 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 (*spec <= '9' && *spec >= '0') { /* opt:index */
if (index) if (indexptr)
*index = strtol(spec, NULL, 0); *indexptr = spec;
return 1; return 1;
} else if (*spec == 'v' || *spec == 'a' || *spec == 's' || *spec == 'd' || } else if (*spec == 'v' || *spec == 'a' || *spec == 's' || *spec == 'd' ||
*spec == 't' || *spec == 'V') { /* opt:[vasdtV] */ *spec == 't' || *spec == 'V') { /* opt:[vasdtV] */
...@@ -5127,6 +5128,9 @@ static int match_stream_specifier(AVFormatContext *s, AVStream *st, ...@@ -5127,6 +5128,9 @@ static int match_stream_specifier(AVFormatContext *s, AVStream *st,
case 'V': type = AVMEDIA_TYPE_VIDEO; nopic = 1; break; case 'V': type = AVMEDIA_TYPE_VIDEO; nopic = 1; break;
default: av_assert0(0); 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 #if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS FF_DISABLE_DEPRECATION_WARNINGS
if (type != st->codecpar->codec_type if (type != st->codecpar->codec_type
...@@ -5139,37 +5143,39 @@ FF_ENABLE_DEPRECATION_WARNINGS ...@@ -5139,37 +5143,39 @@ FF_ENABLE_DEPRECATION_WARNINGS
#endif #endif
if (nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC)) if (nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC))
return 0; 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) == ':') { } else if (*spec == 'p' && *(spec + 1) == ':') {
int prog_id, i, j; int prog_id, i, j;
int found = 0;
char *endptr; char *endptr;
spec += 2; spec += 2;
prog_id = strtol(spec, &endptr, 0); 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++) { for (i = 0; i < s->nb_programs; i++) {
if (s->programs[i]->id != prog_id) if (s->programs[i]->id != prog_id)
continue; continue;
for (j = 0; j < s->programs[i]->nb_stream_indexes; j++) { for (j = 0; j < s->programs[i]->nb_stream_indexes; j++) {
if (st->index == s->programs[i]->stream_index[j]) { if (st->index == s->programs[i]->stream_index[j]) {
if (*endptr++ == ':') { // p:<id>:.... found = 1;
return match_stream_specifier(s, st, endptr, index); i = s->nb_programs;
} else { break;
return 1;
}
} }
} }
} }
return 0; if (!found)
return 0;
} else if (*spec == '#' || } else if (*spec == '#' ||
(*spec == 'i' && *(spec + 1) == ':')) { (*spec == 'i' && *(spec + 1) == ':')) {
int stream_id; int stream_id;
char *endptr; char *endptr;
spec += 1 + (*spec == 'i'); spec += 1 + (*spec == 'i');
stream_id = strtol(spec, &endptr, 0); stream_id = strtol(spec, &endptr, 0);
if (!*endptr) if (spec == endptr || *endptr) /* Disallow empty id and make sure we are at the end. */
return stream_id == st->id; return AVERROR(EINVAL);
return stream_id == st->id;
} else if (*spec == 'm' && *(spec + 1) == ':') { } else if (*spec == 'm' && *(spec + 1) == ':') {
AVDictionaryEntry *tag; AVDictionaryEntry *tag;
char *key, *val; char *key, *val;
...@@ -5193,7 +5199,7 @@ FF_ENABLE_DEPRECATION_WARNINGS ...@@ -5193,7 +5199,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
av_freep(&key); av_freep(&key);
return ret; return ret;
} else if (*spec == 'u') { } else if (*spec == 'u' && *(spec + 1) == '\0') {
AVCodecParameters *par = st->codecpar; AVCodecParameters *par = st->codecpar;
#if FF_API_LAVF_AVCTX #if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGS FF_DISABLE_DEPRECATION_WARNINGS
...@@ -5238,40 +5244,54 @@ FF_ENABLE_DEPRECATION_WARNINGS ...@@ -5238,40 +5244,54 @@ FF_ENABLE_DEPRECATION_WARNINGS
#else #else
return par->codec_id != AV_CODEC_ID_NONE && val != 0; return par->codec_id != AV_CODEC_ID_NONE && val != 0;
#endif #endif
} else if (!*spec) /* empty specifier, matches everything */ } else {
return 1; return AVERROR(EINVAL);
}
}
return AVERROR(EINVAL); /* empty specifier, matches everything */
return 1;
} }
int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st, int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st,
const char *spec) const char *spec)
{ {
int ret; int ret, index;
int index = -1; char *endptr;
const char *indexptr = NULL;
/* This is not really needed but saves us a loop for simple stream index specifiers. */ ret = match_stream_specifier(s, st, spec, &indexptr);
if (*spec <= '9' && *spec >= '0') /* opt:index */ if (ret < 0)
return strtol(spec, NULL, 0) == st->index; goto error;
ret = match_stream_specifier(s, st, spec, &index); if (!indexptr)
if (ret < 0) {
av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec);
return ret; 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. */ /* 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++) { 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) if (ret < 0)
return ret; goto error;
if (ret > 0 && index-- == 0 && st == s->streams[i]) if (ret > 0 && index-- == 0 && st == s->streams[i])
return 1; return 1;
} }
return 0; 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) int ff_generate_avci_extradata(AVStream *st)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment