From patchwork Tue Jan 9 18:27:28 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacob Trimble X-Patchwork-Id: 7235 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.78.2 with SMTP id r2csp4237854jaa; Tue, 9 Jan 2018 10:27:45 -0800 (PST) X-Google-Smtp-Source: ACJfBose5fl2osYkLxGDcwjLIdvMBGDgvqrwEoX+zfQFSlKsIsKqPwQxRWQYa3rbNwvGcza4b6Id X-Received: by 10.28.87.146 with SMTP id l140mr11986091wmb.86.1515522465235; Tue, 09 Jan 2018 10:27:45 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1515522465; cv=none; d=google.com; s=arc-20160816; b=gv0ndLU5mXdOmrg/ojq2tyUMdWjQS8TPBSslLS3FZkKNNmxC8eHnAeazm3CdZO/GSX dZXX6A9n9oa1eHY6R6FxJuOVJLK8JF2Kveg3UVqlTP7/yF+335vWp+6USA5Y5h/w52Pn F1YFxwdSfIQwjz3uN+lJIekwdThNFcMW6bw6WErsCuj6mSGyKaMNnvWiQv/yoGo4Sw/3 K5/VON5//e1MRLSMWT2fZSVYNiYzc+rQJgWshzerkUQExqrwabrHkgzmSSSBN6XiZvSY ONK8c8wVV5k9E1iNBYzacUJFTtBbfppW9yIYyPxF5sqhy0TcFva/TQIbnxAgtdAzc7Pn cdYw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject:to :message-id:date:from:references:in-reply-to:mime-version :dkim-signature:delivered-to:arc-authentication-results; bh=bkKD2NPsj0HQEeKc2AmmGpPjB2i6y7HRntB4AGzeI1U=; b=wjcXsdnLIaRtSlF7ShkA0EbTe8xDnRIelPbvBBokDKETXoaWhrxj3gvgiojPE9nsl8 o7OcL3Iu65zzjWk3o7XpGM2BbX4pwr3BzDUTO7qXTiLJRvbNMhawpIpgyQbnlnga3IzN g8aVYwYzU6PpydibtWHf6au1y/HxfdbhBaqHlBdjasFBQcHpbtRI6lHIlLZSRI0aQBcQ Ul/FP433rraZp+NPaV4f5x0EYu1WyfqYKKjp+43vANLfnZW/+qCJFXiVE34gS3dsrkkY ZEf3DyyX/h3iyL2wyAqYDwwqTLot0TEnpFzqa5+tBBd++E8u+zI/95T6mip5P4PDlRpI 5bBQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@google.com header.s=20161025 header.b=PSJfj8y1; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id h26si9769015wmc.245.2018.01.09.10.27.44; Tue, 09 Jan 2018 10:27:45 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@google.com header.s=20161025 header.b=PSJfj8y1; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 16945689C28; Tue, 9 Jan 2018 20:27:36 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-vk0-f44.google.com (mail-vk0-f44.google.com [209.85.213.44]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id E8987680485 for ; Tue, 9 Jan 2018 20:27:28 +0200 (EET) Received: by mail-vk0-f44.google.com with SMTP id p144so9976187vke.11 for ; Tue, 09 Jan 2018 10:27:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=mime-version:in-reply-to:references:from:date:message-id:subject:to; bh=Wfjb3pEu4G1+3tD2biSejpJEO/DYJ7YaRu68S8fQ21k=; b=PSJfj8y1CN1xAuaM/A0La2CfzcavhDjybzEq55POZmQ9CE/tDRNSQZk8513EepZjvg 6eTrreODpKHRj9Bkt3u1Z9FfUYJQjI57aqJqsHlTW7oHe+uFdmGZ1lEJqaDmSRot/V05 amPQ9VNlcIVlViAbUfceK9WoTyd37qOk3TZtUm+NWsBg4yvrXvLz1sTfQENTvgRC5Wmr lqWSBw0A5Y8qF8n2nWP5bBB6hHfQE9whLMIYn7QFeWMgVeK0coqItIF8FM5UmmsO4OFl xCsf8iNX4WLYl0FZPGuVFHYxNXR39PjdLHvniGSr6Ml01XF6wppPJ3GpjMIaqt+ZSZCy OlHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to; bh=Wfjb3pEu4G1+3tD2biSejpJEO/DYJ7YaRu68S8fQ21k=; b=GXc6BucpNs5M+/js2ii8JH5xqOoesTTBfI14bFk0R+2NspJw1XLLHaKA4FdiW1J3KU A0QgUNr1zdZgHEDBfLDQ+ddswv7FaRpUaMtqx1xEptdKxnRhKWTBV9VZN0cEATccKuaL 8feucHu/5XioxKMYbmXrFkzgecYUDKKImbUrvqDq1HtAty+KKoSs5zM6bMIjY6plQYq2 4+VtM1cola1HtDdXFKPafyQbS8wDhxFbCl+CPRjaTK2JqUaFKZxRIvBeH6fGC5MAKYXE iPlPb69b6EYsTxxJmzcnQE8EhpdN6Y7yQYov+p1HxLM1acHL0Zx9Q6dSmjcwPOGxB3fw SOGg== X-Gm-Message-State: AKwxytd7EYqFI4E3Qdnh+W7O/82Ns1eWI0aR3rRWYjeiVsF09bf4u4Tz PDO8UmjoTEo1F0yB7JUU/jXqUTCmC2BCpM30vWW+9I5x X-Received: by 10.31.149.144 with SMTP id x138mr13772048vkd.121.1515522448832; Tue, 09 Jan 2018 10:27:28 -0800 (PST) MIME-Version: 1.0 Received: by 10.103.212.13 with HTTP; Tue, 9 Jan 2018 10:27:28 -0800 (PST) In-Reply-To: References: <20180105194928.104085-1-modmaker@google.com> <20180105194928.104085-2-modmaker@google.com> From: Jacob Trimble Date: Tue, 9 Jan 2018 10:27:28 -0800 Message-ID: To: FFmpeg development discussions and patches Subject: Re: [FFmpeg-devel] [PATCH 2/3] avformat/mov: Fix parsing of saio/siaz atoms in encrypted content. 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" On Mon, Jan 8, 2018 at 5:39 PM, Carl Eugen Hoyos wrote: > 2018-01-08 23:34 GMT+01:00 Jacob Trimble : >> On Fri, Jan 5, 2018 at 3:41 PM, Carl Eugen Hoyos wrote: >>> 2018-01-05 23:58 GMT+01:00 Jacob Trimble : >>>> On Fri, Jan 5, 2018 at 2:01 PM, Carl Eugen Hoyos wrote: >>>>> 2018-01-05 22:29 GMT+01:00 Jacob Trimble : >>>>>> On Fri, Jan 5, 2018 at 12:41 PM, Carl Eugen Hoyos wrote: >>>>>>> 2018-01-05 20:49 GMT+01:00 Jacob Trimble : >>>>>>> >>>>>>>> + entry_count = avio_rb32(pb); >>>>>>>> + encryption_index->auxiliary_offsets = av_malloc_array(sizeof(size_t), entry_count); >>>>>>> >>>>>>> (sizeof(variable) instead of sizeof(type), please.) >>>>>>> >>>>>>> But since this could be used for a dos attack, please change this >>>>>>> to something similar to 1112ba01. >>>>>>> If it is easy to avoid it, very short files should not allocate >>>>>>> gigabytes. >>>>>> >>>>>> Switched to calculating the size based on the number of remaining >>>>>> bytes and returning an error if it doesn't match what is read. >>>>> >>>>> Sorry if I miss something: >>>>> >>>>>> + entry_count = (atom.size - 8 - (has_saio_type ? 8 : 0)) / (version == 0 ? 4 : 8); >>>>>> + if (avio_rb32(pb) != entry_count) { >>>>>> + av_log(c->fc, AV_LOG_ERROR, "incorrect entry_count in saio\n"); >>>>>> + return AVERROR_INVALIDDATA; >>>>>> + } >>>>>> + encryption_index->auxiliary_offsets = >>>>>> + av_malloc_array(sizeof(*encryption_index->auxiliary_offsets), entry_count); >>>>> >>>>> Does this avoid a 1G allocation for a file of a few bytes? >>>>> >>>>> Didn't you simply increase the number of needed bytes to change in a valid >>>>> mov file to behave maliciously from one to two? >>>> >>>> From what I can tell, the mov_read_default method (which is what reads >>>> child atoms) will verify "atom.size" to fit inside the parent atom. >>>> This means that for "atom.size" to be excessively large for the file >>>> size, the input would need to be non-seekable (since I think the >>>> top-level atom uses the file size) and all the atoms would need to >>>> have invalid sizes. >>> >>> (I did not check this but I am not convinced the sample I fixed last >>> week is so sophisticated.) >>> >>>> But technically I guess it is possible. >>> >>> Thank you. >>> >>>> But this is basically malloc some number of bytes then read that many >>>> bytes. The only alternative I can think of (in the face of >>>> non-seekable inputs) is to try-read in chunks until we hit EOF or the >>>> end of the expected size. This seems like a lot of extra work that is >>> >>>> not mirrored elsewhere. >>> >>> On the contrary. >>> >>> But you are right, I forgot to write that you have to add an "if (!eof)" >>> to the reading loops, see the function that above commit changed. >>> >>>> There are several cases where this isn't explicitly checked. >>> >>> Yes, and they will be fixed, please don't add another one. >>> >>>> Also, how does this attack work? If the number is way too big, well >>>> get EOM and error out. >>> >>> Which not only causes dos but also makes checking for other (if you >>> like: more dangerous) issues more difficult which is bad. We already >>> know that there are cases where the issue is hard to avoid, I believe >>> this is not such a case. >>> >>> It is possible to create (valid) samples that allocate a huge amount >>> of memory but very small files should not immediately allocate an >>> amount of several G. >> >> Done. > > + entry_count = avio_rb32(pb); > > This has to be checked for later overflow, same in other spots. Done > > + encryption_index->auxiliary_offsets = > + av_malloc_array(sizeof(*encryption_index->auxiliary_offsets), > entry_count); > > I believe you forgot to remove these two lines. Yep, done. > > Note that I chose "1024*1024" arbitrarily to avoid a speed-loss for > (most likely) all valid files when fixing the dos in 1112ba01. > I don't know what a good lower limit for your use-case can be, or > if only using av_fast_realloc() - without the high starting value - > makes sense. Ok, for the saio atoms, it will likely be small (changed it to 1024) since it will either be the number of tenc atoms or the number of chunks or 1. Later code actually only supports one offset, but I didn't want to hard-code that requirement here. > > Carl Eugen > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel From 76c6870513481c14c5c134f1b8e7e9a91e17e6b5 Mon Sep 17 00:00:00 2001 From: Jacob Trimble Date: Wed, 6 Dec 2017 16:17:54 -0800 Subject: [PATCH 2/3] avformat/mov: Fix parsing of saio/siaz atoms in encrypted content. This doesn't support saio atoms with more than one offset. Signed-off-by: Jacob Trimble --- libavformat/isom.h | 6 ++ libavformat/mov.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 243 insertions(+) diff --git a/libavformat/isom.h b/libavformat/isom.h index 3794b1f0fd..3de8053da2 100644 --- a/libavformat/isom.h +++ b/libavformat/isom.h @@ -114,6 +114,12 @@ typedef struct MOVEncryptionIndex { // settings will be used. unsigned int nb_encrypted_samples; AVEncryptionInfo **encrypted_samples; + + uint8_t* auxiliary_info_sizes; + size_t auxiliary_info_sample_count; + uint8_t auxiliary_info_default_size; + size_t* auxiliary_offsets; ///< Absolute seek position + size_t auxiliary_offsets_count; } MOVEncryptionIndex; typedef struct MOVFragmentStreamInfo { diff --git a/libavformat/mov.c b/libavformat/mov.c index 56459742bc..750b8a0860 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -5863,6 +5863,228 @@ end: return ret; } +static int mov_parse_auxiliary_info(MOVContext *c, MOVStreamContext *sc, AVIOContext *pb, MOVEncryptionIndex *encryption_index) +{ + AVEncryptionInfo **sample, **encrypted_samples; + int64_t prev_pos; + size_t sample_count, sample_info_size, i; + int ret = 0; + unsigned int alloc_size = 0; + + if (encryption_index->nb_encrypted_samples) + return 0; + sample_count = encryption_index->auxiliary_info_sample_count; + if (encryption_index->auxiliary_offsets_count != 1) { + av_log(c->fc, AV_LOG_ERROR, "Multiple auxiliary info chunks are not supported\n"); + return AVERROR_PATCHWELCOME; + } + if (sample_count >= INT_MAX / sizeof(*encrypted_samples)) + return AVERROR(ENOMEM); + + prev_pos = avio_tell(pb); + if (!(pb->seekable & AVIO_SEEKABLE_NORMAL) || + avio_seek(pb, encryption_index->auxiliary_offsets[0], SEEK_SET) != encryption_index->auxiliary_offsets[0]) { + av_log(c->fc, AV_LOG_INFO, "Failed to seek for auxiliary info, will only parse senc atoms for encryption info\n"); + goto finish; + } + + for (i = 0; i < sample_count && !pb->eof_reached; i++) { + unsigned int min_samples = FFMIN(FFMAX(i, 1024 * 1024), sample_count); + encrypted_samples = av_fast_realloc(encryption_index->encrypted_samples, &alloc_size, + min_samples * sizeof(*encrypted_samples)); + if (!encrypted_samples) { + ret = AVERROR(ENOMEM); + goto finish; + } + encryption_index->encrypted_samples = encrypted_samples; + + sample = &encryption_index->encrypted_samples[i]; + sample_info_size = encryption_index->auxiliary_info_default_size + ? encryption_index->auxiliary_info_default_size + : encryption_index->auxiliary_info_sizes[i]; + + ret = mov_read_sample_encryption_info(c, pb, sc, sample, sample_info_size > sc->cenc.per_sample_iv_size); + if (ret < 0) + goto finish; + } + if (pb->eof_reached) { + av_log(c->fc, AV_LOG_ERROR, "Hit EOF while reading auxiliary info\n"); + ret = AVERROR_INVALIDDATA; + } else { + encryption_index->nb_encrypted_samples = sample_count; + } + +finish: + avio_seek(pb, prev_pos, SEEK_SET); + if (ret < 0) { + for (; i > 0; i--) { + av_encryption_info_free(encryption_index->encrypted_samples[i - 1]); + } + av_freep(&encryption_index->encrypted_samples); + } + return ret; +} + +/** + * Tries to read the given number of bytes from the stream and puts it in a + * newly allocated buffer. This reads in small chunks to avoid allocating large + * memory if the file contains an invalid/malicious size value. + */ +static int mov_try_read_block(AVIOContext *pb, size_t size, uint8_t **data) +{ + const unsigned int block_size = 1024 * 1024; + uint8_t *buffer = NULL; + unsigned int alloc_size = 0, offset = 0; + while (offset < size) { + unsigned int new_size = + alloc_size >= INT_MAX - block_size ? INT_MAX : alloc_size + block_size; + uint8_t *new_buffer = av_fast_realloc(buffer, &alloc_size, new_size); + unsigned int to_read = FFMIN(size, alloc_size) - offset; + if (!new_buffer) { + av_free(buffer); + return AVERROR(ENOMEM); + } + buffer = new_buffer; + + if (avio_read(pb, buffer + offset, to_read) != to_read) { + av_free(buffer); + return AVERROR_INVALIDDATA; + } + offset += to_read; + } + + *data = buffer; + return 0; +} + +static int mov_read_saiz(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + MOVEncryptionIndex *encryption_index; + MOVStreamContext *sc; + int ret; + unsigned int sample_count; + + ret = get_current_encryption_info(c, &encryption_index, &sc); + if (ret != 1) + return ret; + + if (encryption_index->nb_encrypted_samples) { + // This can happen if we have both saio/saiz and senc atoms. + av_log(c->fc, AV_LOG_DEBUG, "Ignoring duplicate encryption info in saiz\n"); + return 0; + } + + if (encryption_index->auxiliary_info_sample_count) { + av_log(c->fc, AV_LOG_ERROR, "Duplicate saiz atom\n"); + return AVERROR_INVALIDDATA; + } + + avio_r8(pb); /* version */ + if (avio_rb24(pb) & 0x01) { /* flags */ + if (avio_rb32(pb) != sc->cenc.default_encrypted_sample->scheme) { + av_log(c->fc, AV_LOG_DEBUG, "Ignoring saiz box with non-zero aux_info_type\n"); + return 0; + } + if (avio_rb32(pb) != 0) { + av_log(c->fc, AV_LOG_DEBUG, "Ignoring saiz box with non-zero aux_info_type_parameter\n"); + return 0; + } + } + + encryption_index->auxiliary_info_default_size = avio_r8(pb); + sample_count = avio_rb32(pb); + encryption_index->auxiliary_info_sample_count = sample_count; + + if (encryption_index->auxiliary_info_default_size == 0) { + ret = mov_try_read_block(pb, sample_count, &encryption_index->auxiliary_info_sizes); + if (ret < 0) { + av_log(c->fc, AV_LOG_ERROR, "Failed to read the auxiliary info\n"); + return ret; + } + } + + if (encryption_index->auxiliary_offsets_count) { + return mov_parse_auxiliary_info(c, sc, pb, encryption_index); + } + + return 0; +} + +static int mov_read_saio(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + uint64_t *auxiliary_offsets; + MOVEncryptionIndex *encryption_index; + MOVStreamContext *sc; + int i, ret; + unsigned int version, entry_count, alloc_size = 0; + + ret = get_current_encryption_info(c, &encryption_index, &sc); + if (ret != 1) + return ret; + + if (encryption_index->nb_encrypted_samples) { + // This can happen if we have both saio/saiz and senc atoms. + av_log(c->fc, AV_LOG_DEBUG, "Ignoring duplicate encryption info in saio\n"); + return 0; + } + + if (encryption_index->auxiliary_offsets_count) { + av_log(c->fc, AV_LOG_ERROR, "Duplicate saio atom\n"); + return AVERROR_INVALIDDATA; + } + + version = avio_r8(pb); /* version */ + if (avio_rb24(pb) & 0x01) { /* flags */ + if (avio_rb32(pb) != sc->cenc.default_encrypted_sample->scheme) { + av_log(c->fc, AV_LOG_DEBUG, "Ignoring saio box with non-zero aux_info_type\n"); + return 0; + } + if (avio_rb32(pb) != 0) { + av_log(c->fc, AV_LOG_DEBUG, "Ignoring saio box with non-zero aux_info_type_parameter\n"); + return 0; + } + } + + entry_count = avio_rb32(pb); + if (entry_count >= INT_MAX / sizeof(*auxiliary_offsets)) + return AVERROR(ENOMEM); + + for (i = 0; i < entry_count && !pb->eof_reached; i++) { + unsigned int min_offsets = FFMIN(FFMAX(i, 1024), entry_count); + auxiliary_offsets = av_fast_realloc( + encryption_index->auxiliary_offsets, &alloc_size, + min_offsets * sizeof(*auxiliary_offsets)); + if (!auxiliary_offsets) { + av_freep(&encryption_index->auxiliary_offsets); + return AVERROR(ENOMEM); + } + encryption_index->auxiliary_offsets = auxiliary_offsets; + + if (version == 0) { + encryption_index->auxiliary_offsets[i] = avio_rb32(pb); + } else { + encryption_index->auxiliary_offsets[i] = avio_rb64(pb); + } + if (c->frag_index.current >= 0) { + encryption_index->auxiliary_offsets[i] += c->fragment.base_data_offset; + } + } + + if (pb->eof_reached) { + av_log(c->fc, AV_LOG_ERROR, "Hit EOF while reading saio\n"); + av_freep(&encryption_index->auxiliary_offsets); + return AVERROR_INVALIDDATA; + } + + encryption_index->auxiliary_offsets_count = entry_count; + + if (encryption_index->auxiliary_info_sample_count) { + return mov_parse_auxiliary_info(c, sc, pb, encryption_index); + } + + return 0; +} + static int mov_read_schm(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; @@ -6078,6 +6300,17 @@ static int cenc_filter(MOVContext *mov, MOVStreamContext *sc, AVPacket *pkt, int } if (encryption_index) { + if (encryption_index->auxiliary_info_sample_count && + !encryption_index->nb_encrypted_samples) { + av_log(mov->fc, AV_LOG_ERROR, "saiz atom found without saio\n"); + return AVERROR_INVALIDDATA; + } + if (encryption_index->auxiliary_offsets_count && + !encryption_index->nb_encrypted_samples) { + av_log(mov->fc, AV_LOG_ERROR, "saio atom found without saiz\n"); + return AVERROR_INVALIDDATA; + } + if (!encryption_index->nb_encrypted_samples) { // Full-sample encryption with default settings. encrypted_sample = sc->cenc.default_encrypted_sample; @@ -6222,6 +6455,8 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('s','i','n','f'), mov_read_default }, { MKTAG('f','r','m','a'), mov_read_frma }, { MKTAG('s','e','n','c'), mov_read_senc }, +{ MKTAG('s','a','i','z'), mov_read_saiz }, +{ MKTAG('s','a','i','o'), mov_read_saio }, { MKTAG('s','c','h','m'), mov_read_schm }, { MKTAG('s','c','h','i'), mov_read_default }, { MKTAG('t','e','n','c'), mov_read_tenc }, @@ -6617,6 +6852,8 @@ static void mov_free_encryption_index(MOVEncryptionIndex **index) { av_encryption_info_free((*index)->encrypted_samples[i]); } av_freep(&(*index)->encrypted_samples); + av_freep(&(*index)->auxiliary_info_sizes); + av_freep(&(*index)->auxiliary_offsets); av_freep(index); } -- 2.16.0.rc1.238.g530d649a79-goog