This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit 105b6fcd9c5940a3fda1eddfef146055e0e24a3d
Author:     Leo Izen <[email protected]>
AuthorDate: Sun Dec 7 15:47:43 2025 -0500
Commit:     Leo Izen <[email protected]>
CommitDate: Sat Dec 20 11:53:21 2025 -0500

    avcodec/exif: avoid leaking EXIF metadata upon parse failure
    
    Before this commit, exif_parse_ifd_list didn't free *ifd upon failure,
    relying on the caller to do so instead. We only guarded some of the
    calls against this function, not all of them, so sometimes it leaked.
    
    This commit fixes this, so exif_parse_ifd_list freeds *ifd upon failure
    so callers do not have to guard its invocation with a free wrapper.
    
    Fixes: ossfuzz 440747118: Integer-overflow in av_strerror
    
    Signed-off-by: Leo Izen <[email protected]>
---
 libavcodec/exif.c | 43 +++++++++++++++++++++++++++++++------------
 1 file changed, 31 insertions(+), 12 deletions(-)

diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index 93e1050d1f..38438661cc 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -511,7 +511,6 @@ static int exif_decode_tag(void *logctx, GetByteContext 
*gb, int le,
              * but we were probably incorrect at this
              * point so we try again as a binary blob
              */
-            av_exif_free(&entry->value.ifd);
             av_log(logctx, AV_LOG_DEBUG, "unrecognized MakerNote IFD, retrying 
as blob\n");
             is_ifd = 0;
         }
@@ -537,41 +536,50 @@ static int exif_parse_ifd_list(void *logctx, 
GetByteContext *gb, int le,
     uint32_t entries;
     size_t required_size;
     void *temp;
+    int ret = 0;
 
     av_log(logctx, AV_LOG_DEBUG, "parsing IFD list at offset: %d\n", 
bytestream2_tell(gb));
 
     if (bytestream2_get_bytes_left(gb) < 2) {
         av_log(logctx, guess ? AV_LOG_DEBUG : AV_LOG_ERROR,
                "not enough bytes remaining in EXIF buffer: 2 required\n");
-        return AVERROR_INVALIDDATA;
+        ret = AVERROR_INVALIDDATA;
+        goto end;
     }
 
     entries = ff_tget_short(gb, le);
     if (bytestream2_get_bytes_left(gb) < entries * BASE_TAG_SIZE) {
         av_log(logctx, guess ? AV_LOG_DEBUG : AV_LOG_ERROR,
                "not enough bytes remaining in EXIF buffer. entries: %" PRIu32 
"\n", entries);
-        return AVERROR_INVALIDDATA;
+        ret = AVERROR_INVALIDDATA;
+        goto end;
     }
     if (entries > 4096) {
         /* that is a lot of entries, probably an error */
         av_log(logctx, guess ? AV_LOG_DEBUG : AV_LOG_ERROR,
                "too many entries: %" PRIu32 "\n", entries);
-        return AVERROR_INVALIDDATA;
+        ret = AVERROR_INVALIDDATA;
+        goto end;
     }
 
     ifd->count = entries;
     av_log(logctx, AV_LOG_DEBUG, "entry count for IFD: %u\n", ifd->count);
 
     /* empty IFD is technically legal but equivalent to no metadata present */
-    if (!ifd->count)
+    if (!ifd->count) {
+        ret = 0;
         goto end;
+    }
 
-    if (av_size_mult(ifd->count, sizeof(*ifd->entries), &required_size) < 0)
-        return AVERROR(ENOMEM);
+    if (av_size_mult(ifd->count, sizeof(*ifd->entries), &required_size) < 0) {
+        ret = AVERROR(ENOMEM);
+        goto end;
+    }
     temp = av_fast_realloc(ifd->entries, &ifd->size, required_size);
     if (!temp) {
         av_freep(&ifd->entries);
-        return AVERROR(ENOMEM);
+        ret = AVERROR(ENOMEM);
+        goto end;
     }
     ifd->entries = temp;
 
@@ -580,17 +588,29 @@ static int exif_parse_ifd_list(void *logctx, 
GetByteContext *gb, int le,
     memset(ifd->entries, 0, required_size);
 
     for (uint32_t i = 0; i < entries; i++) {
-        int ret = exif_decode_tag(logctx, gb, le, depth, &ifd->entries[i]);
+        ret = exif_decode_tag(logctx, gb, le, depth, &ifd->entries[i]);
         if (ret < 0)
-            return ret;
+            goto end;
     }
 
 end:
+    if (ret < 0) {
+        av_exif_free(ifd);
+        return ret;
+    }
     /*
      * at the end of an IFD is an pointer to the next IFD
      * or zero if there are no more IFDs, which is usually the case
      */
-    return ff_tget_long(gb, le);
+    ret = ff_tget_long(gb, le);
+
+    /* overflow */
+    if (ret < 0) {
+        ret = AVERROR_INVALIDDATA;
+        av_exif_free(ifd);
+    }
+
+    return ret;
 }
 
 /*
@@ -816,7 +836,6 @@ int av_exif_parse_buffer(void *logctx, const uint8_t *buf, 
size_t size,
      */
     ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, ifd, 0);
     if (ret < 0) {
-        av_exif_free(ifd);
         av_log(logctx, AV_LOG_ERROR, "error decoding EXIF data: %s\n", 
av_err2str(ret));
         return ret;
     }

_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to