From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id 317FD48276 for ; Mon, 22 Dec 2025 02:30:56 +0000 (UTC) Authentication-Results: ffbox; dkim=fail (body hash mismatch (got b'iULPxVfvzLpvEIxinW3JPeICT5yJMAOAAcGmUrWtT88=', expected b'kBoTSEiKlgUxWf0Ez1gXan8+Nclm3WR58sQA1jBB3JM=')) header.d=ffmpeg.org header.i=@ffmpeg.org header.a=rsa-sha256 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1766370634; h=mime-version : to : date : message-id : reply-to : subject : list-id : list-archive : list-archive : list-help : list-owner : list-post : list-subscribe : list-unsubscribe : from : cc : content-type : content-transfer-encoding : from; bh=iULPxVfvzLpvEIxinW3JPeICT5yJMAOAAcGmUrWtT88=; b=Hf8bFdCknVw8X4MaGR7SjAdGSy+XiZ29it3OwxNr9kpUJ9O7iTzLsPzO2uUxGrB1szfZd fMJZCZQAA1fbuqEn/7Z2DkvLMHkAir05Z6V21HErCzsxt+4b9p5n/oo8gNtXSoSF6T2v6q6 OqyNxhSsST6lBlbt2QwaJd9BksEfeduxd1lCCcr+BF/DIgEavbYXdqN5RptlQSnWarMex0K hZ5N1fWSyvxD6Gm0eakrr8Gg5clFQjXFB7ytzHdfvNXM7zcgsCGOHEVpWzjmeXz2jZsgpOU USA7JjxBz4KlEqVty63OI6dWD1mnhwbrqXKMO35uwP3ivmGJuIejuO/zCRXg== Received: from [172.20.0.2] (unknown [172.19.0.4]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id 01022690A7A; Mon, 22 Dec 2025 04:30:33 +0200 (EET) ARC-Seal: i=1; cv=none; a=rsa-sha256; d=ffmpeg.org; s=arc; t=1766370624; b=REnCcKIBCe54WJHq/t+ilFPVN+5MxmkciuPgMZx2pGlFfeUodPxsm459tkXNwbKJniaip 2xiZIXx/VPuCOkbRQbidUSiktlgjeRN/Cb5rjN+UtDOfboPc6JdCVSjlfbYSNdXrFvyv3pg jDfvX/A7itJJ9470DPHGFK8V6nH3vyIc1tnYmZ3nLs+E4rAh6jPIDDAZtCItQBSrvhYPwK/ 8iYBjblJklxiyzLdg4stGqmAEF0iJOej2iuTsDE054gKf61Nrhs/cTGysi6opNpahKS7ZZS Nt8WsRzMvZcCRC/N+TnWNXI0qgq9p1PeIdERweCsJCnE4+tlXtTcFLDJu2Mw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=ffmpeg.org; s=arc; t=1766370624; h=from : sender : reply-to : subject : date : message-id : to : cc : mime-version : content-type : content-transfer-encoding : content-id : content-description : resent-date : resent-from : resent-sender : resent-to : resent-cc : resent-message-id : in-reply-to : references : list-id : list-help : list-unsubscribe : list-subscribe : list-post : list-owner : list-archive; bh=1Xv7DCxXHKWhtXbx+pkS4rKaMV+lOwAT/jQDICz/tmo=; b=cDfvzyajDzRhdOKmbEw9Y1+nw1oFnIfMOQlOcRNeQtgLv5SVTRTVx759SnTKAeW3Qhi3k +MbLMUdH7wTE0PhjEd56NUVOkFYKohrqPVOHl9LsBNGh2JeQLkiUn2KJpB7sMSXpSvmG2b1 CtvdBWBmhp/OIJZwFpXQouDOFzyoUXj1Ct0uRaOQtV6Z9L1ae+mCeuokIYMxzWiy8VzaXdA hUp3FPNgwu6rtsIY9OEA5ta+tmYA3nnqy12Tr7LJTeaCQIYli4sqmTeN0yUmR2nltOFMeS+ OXQFgFZSC/VZ7c/HUjv0P5G2uytITUTihFoUntszuGuNbJSox2YyOkpstNpA== ARC-Authentication-Results: i=1; ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none; dmarc=pass header.from=ffmpeg.org policy.dmarc=quarantine Authentication-Results: ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none (Message is not ARC signed); dmarc=pass (Used From Domain Record) header.from=ffmpeg.org policy.dmarc=quarantine DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1766370615; h=content-type : mime-version : content-transfer-encoding : from : to : reply-to : subject : date : from; bh=kBoTSEiKlgUxWf0Ez1gXan8+Nclm3WR58sQA1jBB3JM=; b=CboqC1iHmIMY5CXXbPVAFgwEcql98Y874v3rY9dRjR+rp1Sl0YX3VZOPsZDfdMLEqWEYQ kwc5/gH92x1nlK/+OZMDuF2HtsPvDJiG0Pq8Ic+3nCrvYfLCD3fD+ne8l6V1Du1AWYyzJrH 4lGz9lgRJ7ElWS4n3j8NUsWpGxUeyCLqmwNDQeeUrRMWOrpa//bZPqJwib5q2BmtA+mLObh sy5qrddLO0mlFiIFGMFDLyyNlOXRKdlKq3bYKd3CJ2D0HrIGcbWdMBpivDmFhSOS/q49CmI JkC6ZKr1f+O9SLAkq6VXct3YyqPlVQCMMstan802eI59dbiue/2ZjYlivjZg== Received: from 55ca25703178 (code.ffmpeg.org [188.245.149.3]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id 37532690A75 for ; Mon, 22 Dec 2025 04:30:15 +0200 (EET) MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Date: Mon, 22 Dec 2025 02:30:14 -0000 Message-ID: <176637061543.60.1248450729003323986@2cb04c0e5124> Message-ID-Hash: XFEM2M35W2O72QCIG23MXY3PUJCHH4RH X-Message-ID-Hash: XFEM2M35W2O72QCIG23MXY3PUJCHH4RH X-MailFrom: code@ffmpeg.org X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-ffmpeg-devel.ffmpeg.org-0; header-match-ffmpeg-devel.ffmpeg.org-1; header-match-ffmpeg-devel.ffmpeg.org-2; header-match-ffmpeg-devel.ffmpeg.org-3; emergency; member-moderation X-Mailman-Version: 3.3.10 Precedence: list Reply-To: FFmpeg development discussions and patches Subject: [FFmpeg-devel] [PATCH] avcodec/exif: size buffer after extra IFD extraction (PR #21258) List-Id: FFmpeg development discussions and patches Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: ruikai via ffmpeg-devel Cc: ruikai Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Archived-At: List-Archive: List-Post: PR #21258 opened by ruikai URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21258 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21258.patch The extra-IFD feature stores trailing IFDs as dummy AV_TIFF_IFD entries. If extraction stops early (e.g. non-contiguous tags or invalid type/count), one of these entries can remain in the IFD while the output size is computed as if it had no directory slot. This can make exif_write_ifd() write past the end of the buffer. Scan the full reserved tag range, validate candidates as single AV_TIFF_IFD entries, and allocate the output buffer after any successful extraction so all directory entries are counted. Repro (ASan): ``` ./configure --toolchain=clang-asan --enable-debug \ --disable-optimizations make -j"$(nproc)" ASAN_OPTIONS=halt_on_error=1:detect_leaks=0 ./ffmpeg_g \ -loglevel error -nostdin -i poc.png -f null - ``` This triggers an OOB write in exif.c:731 (exif_write_ifd). Reference: https://gist.github.com/retr0reg/bc5f5dd9e2afedb09853913f1d1ee246 Regression: 784aa09fa8 Found-by: Ruikai Peng, Pwno >>From d621a482740cd3827b0f42d0f424d1c234e42b2c Mon Sep 17 00:00:00 2001 From: Ruikai Peng Date: Sun, 21 Dec 2025 21:21:01 -0500 Subject: [PATCH] avcodec/exif: size buffer after extra IFD extraction The extra-IFD feature stores trailing IFDs as dummy AV_TIFF_IFD entries. If extraction stops early (e.g. non-contiguous tags or invalid type/count), one of these entries can remain in the IFD while the output size is computed as if it had no directory slot. This can make exif_write_ifd() write past the end of the buffer. Scan the full reserved tag range, validate candidates as single AV_TIFF_IFD entries, and allocate the output buffer after any successful extraction so all directory entries are counted. Repro (ASan): ./configure --toolchain=clang-asan --enable-debug \ --disable-optimizations make -j"$(nproc)" ASAN_OPTIONS=halt_on_error=1:detect_leaks=0 ./ffmpeg_g \ -loglevel error -nostdin -i poc.png -f null - This triggers an OOB write in exif.c:731 (exif_write_ifd). Reference: https://gist.github.com/retr0reg/bc5f5dd9e2afedb09853913f1d1ee246 Regression: 784aa09fa8 Found-by: Ruikai Peng, Pwno --- libavcodec/exif.c | 69 ++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/libavcodec/exif.c b/libavcodec/exif.c index 0de543e35a..b79644ac91 100644 --- a/libavcodec/exif.c +++ b/libavcodec/exif.c @@ -673,9 +673,7 @@ static size_t exif_get_ifd_size(const AVExifMetadata *ifd) for (size_t i = 0; i < ifd->count; i++) { const AVExifEntry *entry = &ifd->entries[i]; if (entry->type == AV_TIFF_IFD) { - /* this is an extra IFD, not an entry, so we don't need to add base tag size */ - size_t base_size = entry->id > 0xFFECu && entry->id <= 0xFFFCu ? 0 : BASE_TAG_SIZE; - total_size += base_size + exif_get_ifd_size(&entry->value.ifd) + entry->ifd_offset; + total_size += BASE_TAG_SIZE + exif_get_ifd_size(&entry->value.ifd) + entry->ifd_offset; } else { size_t payload_size = entry->count * exif_sizes[entry->type]; total_size += BASE_TAG_SIZE + (payload_size > 4 ? payload_size : 0); @@ -759,7 +757,6 @@ int av_exif_write(void *logctx, const AVExifMetadata *ifd, AVBufferRef **buffer, goto end; } - size = exif_get_ifd_size(ifd); switch (header_mode) { case AV_EXIF_EXIF00: off = 6; @@ -776,6 +773,44 @@ int av_exif_write(void *logctx, const AVExifMetadata *ifd, AVBufferRef **buffer, headsize = 0; break; } + + int extras = 0; + for (uint16_t extra_tag = 0xFFFCu; + extra_tag > 0xFFECu && extras < FF_ARRAY_ELEMS(extra_ifds); + extra_tag--) { + AVExifEntry *extra_entry = NULL; + + ret = av_exif_get_entry(logctx, (AVExifMetadata *) ifd, extra_tag, 0, &extra_entry); + if (ret <= 0) + continue; + av_log(logctx, AV_LOG_DEBUG, "found extra IFD tag: %04x\n", extra_tag); + if (extra_entry->type != AV_TIFF_IFD || extra_entry->count != 1) { + av_log(logctx, AV_LOG_DEBUG, "invalid extra IFD tag: %04x\n", extra_tag); + continue; + } + if (!ifd_new) { + ifd_new = av_exif_clone_ifd(ifd); + if (!ifd_new) + break; + ifd = ifd_new; + } + /* calling remove_entry will call av_exif_free on the original */ + AVExifMetadata *cloned = av_exif_clone_ifd(&extra_entry->value.ifd); + if (!cloned) + break; + extra_ifds[extras] = *cloned; + /* don't use av_exif_free here, we want to preserve internals */ + av_free(cloned); + ret = av_exif_remove_entry(logctx, ifd_new, extra_tag, 0); + if (ret < 0) + break; + + extras++; + } + + size = exif_get_ifd_size(ifd); + for (int i = 0; i < extras; i++) + size += exif_get_ifd_size(&extra_ifds[i]); buf = av_buffer_alloc(size + off + headsize); if (!buf) { ret = AVERROR(ENOMEM); @@ -798,32 +833,6 @@ int av_exif_write(void *logctx, const AVExifMetadata *ifd, AVBufferRef **buffer, tput32(&pb, le, 8); } - int extras; - for (extras = 0; extras < FF_ARRAY_ELEMS(extra_ifds); extras++) { - AVExifEntry *extra_entry = NULL; - uint16_t extra_tag = 0xFFFCu - extras; - ret = av_exif_get_entry(logctx, (AVExifMetadata *) ifd, extra_tag, 0, &extra_entry); - if (ret <= 0) - break; - av_log(logctx, AV_LOG_DEBUG, "found extra IFD tag: %04x\n", extra_tag); - if (!ifd_new) { - ifd_new = av_exif_clone_ifd(ifd); - if (!ifd_new) - break; - ifd = ifd_new; - } - /* calling remove_entry will call av_exif_free on the original */ - AVExifMetadata *cloned = av_exif_clone_ifd(&extra_entry->value.ifd); - if (!cloned) - break; - extra_ifds[extras] = *cloned; - /* don't use av_exif_free here, we want to preserve internals */ - av_free(cloned); - ret = av_exif_remove_entry(logctx, ifd_new, extra_tag, 0); - if (ret < 0) - break; - } - next = bytestream2_tell_p(&pb); ret = exif_write_ifd(logctx, &pb, le, 0, ifd); if (ret < 0) { -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org