avformat/tedcaptionsdec: Fix leak of AVBPrint upon error

The tedcaptions demuxer uses an AVBPrint whose string is not restricted
to its internal buffer; it therefore needs to be cleaned up, yet this is
not done on error, as parse_file() returned simply returned directly.
This is fixed by going to fail first in such cases.
Furthermore, there is also a second way how this string can leak: By
having more than one subtitle per subtitle block, as the new one simply
overwrites the old one in this case as the AVBPrint is initialized each
time upon encountering a subtitle line. The code has been modified to
simply append the new subtitle to the old one, so that the old one can't
leak any more.

Reviewed-by: Nicolas George <george@nsup.org>
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
This commit is contained in:
Andreas Rheinhardt 2020-09-20 16:16:51 +02:00
parent b0dee629da
commit 9f7e592df2
1 changed files with 36 additions and 37 deletions

View File

@ -94,25 +94,20 @@ static int parse_string(AVIOContext *pb, int *cur_byte, AVBPrint *bp, int full)
{ {
int ret; int ret;
av_bprint_init(bp, 0, full ? AV_BPRINT_SIZE_UNLIMITED : AV_BPRINT_SIZE_AUTOMATIC);
ret = expect_byte(pb, cur_byte, '"'); ret = expect_byte(pb, cur_byte, '"');
if (ret < 0) if (ret < 0)
goto fail; return ret;
while (*cur_byte > 0 && *cur_byte != '"') { while (*cur_byte > 0 && *cur_byte != '"') {
if (*cur_byte == '\\') { if (*cur_byte == '\\') {
next_byte(pb, cur_byte); next_byte(pb, cur_byte);
if (*cur_byte < 0) { if (*cur_byte < 0)
ret = AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
goto fail;
}
if ((*cur_byte | 32) == 'u') { if ((*cur_byte | 32) == 'u') {
unsigned chr = 0, i; unsigned chr = 0, i;
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
next_byte(pb, cur_byte); next_byte(pb, cur_byte);
if (!HEX_DIGIT_TEST(*cur_byte)) { if (!HEX_DIGIT_TEST(*cur_byte))
ret = ERR_CODE(*cur_byte); return ERR_CODE(*cur_byte);
goto fail;
}
chr = chr * 16 + HEX_DIGIT_VAL(*cur_byte); chr = chr * 16 + HEX_DIGIT_VAL(*cur_byte);
} }
av_bprint_utf8(bp, chr); av_bprint_utf8(bp, chr);
@ -126,22 +121,18 @@ static int parse_string(AVIOContext *pb, int *cur_byte, AVBPrint *bp, int full)
} }
ret = expect_byte(pb, cur_byte, '"'); ret = expect_byte(pb, cur_byte, '"');
if (ret < 0) if (ret < 0)
goto fail; return ret;
if (full && !av_bprint_is_complete(bp)) { if (full && !av_bprint_is_complete(bp))
ret = AVERROR(ENOMEM); return AVERROR(ENOMEM);
goto fail;
}
return 0;
fail: return 0;
av_bprint_finalize(bp, NULL);
return ret;
} }
static int parse_label(AVIOContext *pb, int *cur_byte, AVBPrint *bp) static int parse_label(AVIOContext *pb, int *cur_byte, AVBPrint *bp)
{ {
int ret; int ret;
av_bprint_init(bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
ret = parse_string(pb, cur_byte, bp, 0); ret = parse_string(pb, cur_byte, bp, 0);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -195,6 +186,8 @@ static int parse_file(AVIOContext *pb, FFDemuxSubtitlesQueue *subs)
int64_t pos, start, duration; int64_t pos, start, duration;
AVPacket *pkt; AVPacket *pkt;
av_bprint_init(&content, 0, AV_BPRINT_SIZE_UNLIMITED);
next_byte(pb, &cur_byte); next_byte(pb, &cur_byte);
ret = expect_byte(pb, &cur_byte, '{'); ret = expect_byte(pb, &cur_byte, '{');
if (ret < 0) if (ret < 0)
@ -206,34 +199,34 @@ static int parse_file(AVIOContext *pb, FFDemuxSubtitlesQueue *subs)
if (ret < 0) if (ret < 0)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
while (1) { while (1) {
content.size = 0;
start = duration = AV_NOPTS_VALUE; start = duration = AV_NOPTS_VALUE;
ret = expect_byte(pb, &cur_byte, '{'); ret = expect_byte(pb, &cur_byte, '{');
if (ret < 0) if (ret < 0)
return ret; goto fail;
pos = avio_tell(pb) - 1; pos = avio_tell(pb) - 1;
while (1) { while (1) {
ret = parse_label(pb, &cur_byte, &label); ret = parse_label(pb, &cur_byte, &label);
if (ret < 0) if (ret < 0)
return ret; goto fail;
if (!strcmp(label.str, "startOfParagraph")) { if (!strcmp(label.str, "startOfParagraph")) {
ret = parse_boolean(pb, &cur_byte, &start_of_par); ret = parse_boolean(pb, &cur_byte, &start_of_par);
if (ret < 0) if (ret < 0)
return ret; goto fail;
} else if (!strcmp(label.str, "content")) { } else if (!strcmp(label.str, "content")) {
ret = parse_string(pb, &cur_byte, &content, 1); ret = parse_string(pb, &cur_byte, &content, 1);
if (ret < 0) if (ret < 0)
return ret; goto fail;
} else if (!strcmp(label.str, "startTime")) { } else if (!strcmp(label.str, "startTime")) {
ret = parse_int(pb, &cur_byte, &start); ret = parse_int(pb, &cur_byte, &start);
if (ret < 0) if (ret < 0)
return ret; goto fail;
} else if (!strcmp(label.str, "duration")) { } else if (!strcmp(label.str, "duration")) {
ret = parse_int(pb, &cur_byte, &duration); ret = parse_int(pb, &cur_byte, &duration);
if (ret < 0) if (ret < 0)
return ret; goto fail;
} else { } else {
return AVERROR_INVALIDDATA; ret = AVERROR_INVALIDDATA;
goto fail;
} }
skip_spaces(pb, &cur_byte); skip_spaces(pb, &cur_byte);
if (cur_byte != ',') if (cur_byte != ',')
@ -242,18 +235,22 @@ static int parse_file(AVIOContext *pb, FFDemuxSubtitlesQueue *subs)
} }
ret = expect_byte(pb, &cur_byte, '}'); ret = expect_byte(pb, &cur_byte, '}');
if (ret < 0) if (ret < 0)
return ret; goto fail;
if (!content.size || start == AV_NOPTS_VALUE || if (!content.size || start == AV_NOPTS_VALUE ||
duration == AV_NOPTS_VALUE) duration == AV_NOPTS_VALUE) {
return AVERROR_INVALIDDATA; ret = AVERROR_INVALIDDATA;
goto fail;
}
pkt = ff_subtitles_queue_insert(subs, content.str, content.len, 0); pkt = ff_subtitles_queue_insert(subs, content.str, content.len, 0);
if (!pkt) if (!pkt) {
return AVERROR(ENOMEM); ret = AVERROR(ENOMEM);
goto fail;
}
pkt->pos = pos; pkt->pos = pos;
pkt->pts = start; pkt->pts = start;
pkt->duration = duration; pkt->duration = duration;
av_bprint_finalize(&content, NULL); av_bprint_clear(&content);
skip_spaces(pb, &cur_byte); skip_spaces(pb, &cur_byte);
if (cur_byte != ',') if (cur_byte != ',')
@ -262,14 +259,16 @@ static int parse_file(AVIOContext *pb, FFDemuxSubtitlesQueue *subs)
} }
ret = expect_byte(pb, &cur_byte, ']'); ret = expect_byte(pb, &cur_byte, ']');
if (ret < 0) if (ret < 0)
return ret; goto fail;
ret = expect_byte(pb, &cur_byte, '}'); ret = expect_byte(pb, &cur_byte, '}');
if (ret < 0) if (ret < 0)
return ret; goto fail;
skip_spaces(pb, &cur_byte); skip_spaces(pb, &cur_byte);
if (cur_byte != AVERROR_EOF) if (cur_byte != AVERROR_EOF)
return ERR_CODE(cur_byte); ret = ERR_CODE(cur_byte);
return 0; fail:
av_bprint_finalize(&content, NULL);
return ret;
} }
static av_cold int tedcaptions_read_header(AVFormatContext *avf) static av_cold int tedcaptions_read_header(AVFormatContext *avf)