Commit 26bf8651a8 for ffmpeg
commit 26bf8651a8d88f722598c90f6afe7067e9a296dd
Author: Michael Niedermayer <michael@niedermayer.cc>
Date: Sat Oct 10 05:28:20 2026 +0200
avcodec/exif: bound the parsed IFD entries by the size of the EXIF buffer
Every entry of a well formed IFD tree is a distinct 12 byte record
inside the buffer, so a tree cannot have more entries than the buffer
holds records. Count the parsed entries and reject the tree once that
number is exceeded.
Timeout Replicated through UnModified FFmpeg
Found during triage/debugging of OSS-Fuzz issue 561208791
Fixes: Timeout
Fixes: yiiM6atofYKk/nested16.jpg / gen_nested_jpg.py
Fixes: yiiM6atofYKk
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index 8169c4f40d..515235a6aa 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -478,10 +478,12 @@ static int exif_get_makernote_offset(GetByteContext *gb)
}
static int exif_parse_ifd_list(void *logctx, GetByteContext *gb, int le,
- int depth, AVExifMetadata *ifd, int guess);
+ int depth, int *entries_left,
+ AVExifMetadata *ifd, int guess);
static int exif_decode_tag(void *logctx, GetByteContext *gb, int le,
- int depth, AVExifEntry *entry)
+ int depth, int *entries_left, int guess,
+ AVExifEntry *entry)
{
int ret = 0, makernote_offset = -1, tell, is_ifd, count;
enum AVTiffDataType type;
@@ -534,7 +536,8 @@ static int exif_decode_tag(void *logctx, GetByteContext *gb, int le,
return AVERROR(ENOMEM);
bytestream2_get_buffer(gb, entry->ifd_lead, entry->ifd_offset);
}
- ret = exif_parse_ifd_list(logctx, gb, le, depth + 1, &entry->value.ifd, entry->id == MAKERNOTE_TAG);
+ ret = exif_parse_ifd_list(logctx, gb, le, depth + 1, entries_left,
+ &entry->value.ifd, guess || entry->id == MAKERNOTE_TAG);
if (ret < 0 && entry->id == MAKERNOTE_TAG) {
/*
* we guessed that MakerNote was an IFD
@@ -561,7 +564,8 @@ end:
}
static int exif_parse_ifd_list(void *logctx, GetByteContext *gb, int le,
- int depth, AVExifMetadata *ifd, int guess)
+ int depth, int *entries_left,
+ AVExifMetadata *ifd, int guess)
{
uint32_t entries;
size_t required_size;
@@ -591,6 +595,13 @@ static int exif_parse_ifd_list(void *logctx, GetByteContext *gb, int le,
ret = AVERROR_INVALIDDATA;
goto end;
}
+ if (entries > entries_left[guess]) {
+ av_log(logctx, guess ? AV_LOG_DEBUG : AV_LOG_ERROR,
+ "IFD tree has more entries than the EXIF buffer can hold\n");
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+ entries_left[guess] -= entries;
ifd->count = entries;
av_log(logctx, AV_LOG_DEBUG, "entry count for IFD: %u\n", ifd->count);
@@ -618,7 +629,7 @@ 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++) {
- ret = exif_decode_tag(logctx, gb, le, depth, &ifd->entries[i]);
+ ret = exif_decode_tag(logctx, gb, le, depth, entries_left, guess, &ifd->entries[i]);
if (ret < 0)
goto end;
}
@@ -934,7 +945,8 @@ int av_exif_parse_buffer(void *logctx, const uint8_t *buf, size_t size,
*/
int ifd_offsets[17] = { bytestream2_tell(&gbytes) };
int nb_ifds = 1;
- ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, ifd, 0);
+ int entries_left[2] = { size / BASE_TAG_SIZE, size / BASE_TAG_SIZE };
+ ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, entries_left, ifd, 0);
if (ret < 0) {
av_log(logctx, AV_LOG_ERROR, "error decoding EXIF data: %s\n", av_err2str(ret));
return ret;
@@ -954,7 +966,7 @@ int av_exif_parse_buffer(void *logctx, const uint8_t *buf, size_t size,
}
}
ifd_offsets[nb_ifds++] = next;
- ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, &extra_ifd, 1);
+ ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, entries_left, &extra_ifd, 1);
if (ret < 0) {
av_exif_free(&extra_ifd);
break;