From patchwork Tue Jun 25 01:08:56 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Rheinhardt X-Patchwork-Id: 13692 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 6DB02447A91 for ; Tue, 25 Jun 2019 04:10:27 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 43DD06899F9; Tue, 25 Jun 2019 04:10:27 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm1-f68.google.com (mail-wm1-f68.google.com [209.85.128.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id F012D6899CC for ; Tue, 25 Jun 2019 04:10:20 +0300 (EEST) Received: by mail-wm1-f68.google.com with SMTP id h19so1111382wme.0 for ; Mon, 24 Jun 2019 18:10:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=W3oJ0eymAN70RmtABmiMJKGXGMW34NBZEVbLojhFtkA=; b=hiGwL1+UquM/Vr0pbV6qzlakBcqEX4G+8r4p2DAzfQ3wvtoRGREn14/t5FSIOlt3Jb hsuBLuMIeiNj8yD/jGngv3bC1zMfWtRFCFbRHzyhPvhHrx57Dq7g8V+mrunW+ryo0mHC 5xrf7j/15NC+h5HYAplwh0eoA1gJbp2hixuXx769zgWn4WDvVUxtYYPdrwQgSul3+d5a Zwp7mqsq/mJDhJoSZZRp0eKY6AcwIpFGeW5/HcFq7zDSCO7zm3puDzSABGMKnpeDrSTq WIt3ITpQitw7hvez35CU1tkV+jcBM9g6XT8QrfM7xogyqIxUxeJxcIEAUV4LLpT+3FNu 1aCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=W3oJ0eymAN70RmtABmiMJKGXGMW34NBZEVbLojhFtkA=; b=ixhohD7xCEJskvafmykWWO++Ckyv2sj9mjSZ+0NVH1Fyu2FqjCF0wdW7BpS2B2IT9f 7MVjH2wxhOD5Mmz6gP3adj701T0o4DhLD65WcJTkZzuiqf+RjTS70HscnSRmNeF8jkFZ angHm5RkONJMJjNJhlDUMhzSqqu8dOCRc2yvHmYqwtfgHteXz8VIFXLbS1iQpY1S+AjG giqPUEamXUheST84GC5UiW7g9xFDE+LHjwdQ/MS60I8ejwR21SK5PpRhyRWidjd2/wVc ZqIFsI4oRhhFwGtx3hP+t/OO/rxKo/3DicmQYqRGTCOWTJlvTgUy3H15k5kZwo5LCFkz oxRw== X-Gm-Message-State: APjAAAU6QSG72S9o9UrqrMbh8zMqPvXLyN7bsQcfHKuOJLcW4NlGd0Mz e4V7p9edZ0v7tvSadu5ktEJ+hkyN X-Google-Smtp-Source: APXvYqw2gIrunOGL61cd/Jli3eBvaTBSKR51zR0XjWHBvNSp7Nr2tG+4951lLMw5hlFk+yxrOdAsyg== X-Received: by 2002:a1c:e3c1:: with SMTP id a184mr16192114wmh.24.1561425020206; Mon, 24 Jun 2019 18:10:20 -0700 (PDT) Received: from localhost.localdomain (ipbcc063db.dynamic.kabel-deutschland.de. [188.192.99.219]) by smtp.gmail.com with ESMTPSA id 5sm27768346wrc.76.2019.06.24.18.10.19 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Mon, 24 Jun 2019 18:10:19 -0700 (PDT) From: Andreas Rheinhardt To: ffmpeg-devel@ffmpeg.org Date: Tue, 25 Jun 2019 03:08:56 +0200 Message-Id: <20190625010856.43895-1-andreas.rheinhardt@gmail.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: <7747d262-2978-8fdd-959c-cf9d31eacf15@gmail.com> References: <7747d262-2978-8fdd-959c-cf9d31eacf15@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH] avformat/matroskadec: Improve read error/EOF checks I X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" ebml_read_num had a number of flaws: 1. The check for read errors/EOF was totally wrong. E.g. an EBML number beginning with the invalid 0x00 would be considered a read error, although it is just invalid data. 2. The check for read errors/EOF was done just once, after reading the first byte of the EBML number. But errors/EOF can happen inbetween, of course, and this wasn't checked. 3. There was no way to distinguish when EOF should be an error (because the data has to be there) for which an error message should be emitted and when it is not necessarily an error (namely during parsing of EBML IDs). Such a possibility has been added and used. All this was fixed; furthermore, the error messages for invalid EBML numbers were improved and useless initializations were removed. Signed-off-by: Andreas Rheinhardt --- libavformat/matroskadec.c | 74 ++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index 0e9938b65e..59388efb64 100644 --- a/libavformat/matroskadec.c +++ b/libavformat/matroskadec.c @@ -796,33 +796,32 @@ static int ebml_level_end(MatroskaDemuxContext *matroska) * Returns: number of bytes read, < 0 on error */ static int ebml_read_num(MatroskaDemuxContext *matroska, AVIOContext *pb, - int max_size, uint64_t *number) + int max_size, uint64_t *number, int eof_forbidden) { - int read = 1, n = 1; - uint64_t total = 0; + int read, n = 1; + uint64_t total; + int64_t pos; - /* The first byte tells us the length in bytes - avio_r8() can normally - * return 0, but since that's not a valid first ebmlID byte, we can - * use it safely here to catch EOS. */ - if (!(total = avio_r8(pb))) { - /* we might encounter EOS here */ - if (!avio_feof(pb)) { - int64_t pos = avio_tell(pb); - av_log(matroska->ctx, AV_LOG_ERROR, - "Read error at pos. %"PRIu64" (0x%"PRIx64")\n", - pos, pos); - return pb->error ? pb->error : AVERROR(EIO); - } - return AVERROR_EOF; - } + /* The first byte tells us the length in bytes - except when it is zero. */ + total = avio_r8(pb); + if (pb->eof_reached) + goto err; /* get the length of the EBML number */ read = 8 - ff_log2_tab[total]; - if (read > max_size) { - int64_t pos = avio_tell(pb) - 1; - av_log(matroska->ctx, AV_LOG_ERROR, - "Invalid EBML number size tag 0x%02x at pos %"PRIu64" (0x%"PRIx64")\n", - (uint8_t) total, pos, pos); + + if (!total || read > max_size) { + pos = avio_tell(pb) - 1; + if (!total) { + av_log(matroska->ctx, AV_LOG_ERROR, + "0x00 at pos %"PRId64" (0x%"PRIx64") invalid as first byte " + "of an EBML number\n", pos, pos); + } else { + av_log(matroska->ctx, AV_LOG_ERROR, + "Length %d indicated by an EBML number's first byte 0x%02x " + "at pos %"PRId64" (0x%"PRIx64") exceeds max length %d.\n", + read, (uint8_t) total, pos, pos, max_size); + } return AVERROR_INVALIDDATA; } @@ -831,9 +830,29 @@ static int ebml_read_num(MatroskaDemuxContext *matroska, AVIOContext *pb, while (n++ < read) total = (total << 8) | avio_r8(pb); + if (pb->eof_reached) { + eof_forbidden = 1; + goto err; + } + *number = total; return read; + +err: + pos = avio_tell(pb); + if (pb->error) { + av_log(matroska->ctx, AV_LOG_ERROR, + "Read error at pos. %"PRIu64" (0x%"PRIx64")\n", + pos, pos); + return pb->error; + } + if (eof_forbidden) { + av_log(matroska->ctx, AV_LOG_ERROR, "File ended prematurely " + "at pos. %"PRIu64" (0x%"PRIx64")\n", pos, pos); + return AVERROR(EIO); + } + return AVERROR_EOF; } /** @@ -844,7 +863,7 @@ static int ebml_read_num(MatroskaDemuxContext *matroska, AVIOContext *pb, static int ebml_read_length(MatroskaDemuxContext *matroska, AVIOContext *pb, uint64_t *number) { - int res = ebml_read_num(matroska, pb, 8, number); + int res = ebml_read_num(matroska, pb, 8, number, 1); if (res > 0 && *number + 1 == 1ULL << (7 * res)) *number = EBML_UNKNOWN_LENGTH; return res; @@ -986,7 +1005,7 @@ static int matroska_ebmlnum_uint(MatroskaDemuxContext *matroska, { AVIOContext pb; ffio_init_context(&pb, data, size, 0, NULL, NULL, NULL, NULL); - return ebml_read_num(matroska, &pb, FFMIN(size, 8), num); + return ebml_read_num(matroska, &pb, FFMIN(size, 8), num, 1); } /* @@ -1033,7 +1052,7 @@ static int ebml_parse(MatroskaDemuxContext *matroska, EbmlSyntax *syntax, { if (!matroska->current_id) { uint64_t id; - int res = ebml_read_num(matroska, matroska->ctx->pb, 4, &id); + int res = ebml_read_num(matroska, matroska->ctx->pb, 4, &id, 0); if (res < 0) { // in live mode, finish parsing if EOF is reached. return (matroska->is_live && matroska->ctx->pb->eof_reached && @@ -3326,7 +3345,6 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, AVBufferRef *buf int trust_default_duration = 1; if ((n = matroska_ebmlnum_uint(matroska, data, size, &num)) < 0) { - av_log(matroska->ctx, AV_LOG_ERROR, "EBML block data error\n"); return n; } data += n; @@ -3650,7 +3668,7 @@ static int webm_clusters_start_with_keyframe(AVFormatContext *s) AVPacket *pkt; avio_seek(s->pb, cluster_pos, SEEK_SET); // read cluster id and length - read = ebml_read_num(matroska, matroska->ctx->pb, 4, &cluster_id); + read = ebml_read_num(matroska, matroska->ctx->pb, 4, &cluster_id, 1); if (read < 0 || cluster_id != 0xF43B675) // done with all clusters break; read = ebml_read_length(matroska, matroska->ctx->pb, &cluster_length); @@ -3868,7 +3886,7 @@ static int webm_dash_manifest_cues(AVFormatContext *s, int64_t init_range) // cues_end is inclusive and the above sum is reduced by 1. uint64_t cues_length, cues_id; int bytes_read; - bytes_read = ebml_read_num (matroska, matroska->ctx->pb, 4, &cues_id); + bytes_read = ebml_read_num (matroska, matroska->ctx->pb, 4, &cues_id, 1); if (bytes_read < 0 || cues_id != (MATROSKA_ID_CUES & 0xfffffff)) return bytes_read < 0 ? bytes_read : AVERROR_INVALIDDATA; bytes_read = ebml_read_length(matroska, matroska->ctx->pb, &cues_length);