Commit 30fff47764 for ffmpeg
commit 30fff47764e59f02ac7868bb832ed51a88f949fd
Author: Michael Niedermayer <michael@niedermayer.cc>
Date: Tue Oct 20 03:16:27 2026 +0200
avcodec/exif: bound the value bytes of an IFD tree by the size of the EXIF buffer
AISLE-2026-0150-00875 describes the same overlapping entries amplified
further by av_exif_ifd_to_dict(), which formats the values bounded here.
Fixes: 7ReYNupbuA78/testcase/evil.tiff / gen.py
Fixes: 7ReYNupbuA78
Fixes: AISLE-2026-0150-00031
Fixes: memory exhaustion
Memory exhaustion Replicated through UnModified FFmpeg with ASAN
Found-by: Joshua Rogers (AISLE)
The report suggested a cumulative allocation budget per EXIF buffer.
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index 515235a6aa..58151914c5 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -270,10 +270,14 @@ static inline void tput64(PutByteContext *pb, const int le, const uint64_t value
le ? bytestream2_put_le64(pb, value) : bytestream2_put_be64(pb, value);
}
-static int exif_read_values(void *logctx, GetByteContext *gb, int le, AVExifEntry *entry)
+static int exif_read_values(void *logctx, GetByteContext *gb, int le,
+ size_t *bytes_left, AVExifEntry *entry)
{
- if (exif_sizes[entry->type] * entry->count > bytestream2_get_bytes_left(gb))
+ size_t payload_size = exif_sizes[entry->type] * entry->count;
+
+ if (payload_size > bytestream2_get_bytes_left(gb) || payload_size > *bytes_left)
return AVERROR_INVALIDDATA;
+ *bytes_left -= payload_size;
switch (entry->type) {
case AV_TIFF_SHORT:
@@ -478,12 +482,12 @@ static int exif_get_makernote_offset(GetByteContext *gb)
}
static int exif_parse_ifd_list(void *logctx, GetByteContext *gb, int le,
- int depth, int *entries_left,
+ int depth, int *entries_left, size_t *bytes_left,
AVExifMetadata *ifd, int guess);
static int exif_decode_tag(void *logctx, GetByteContext *gb, int le,
- int depth, int *entries_left, int guess,
- AVExifEntry *entry)
+ int depth, int *entries_left, size_t *bytes_left,
+ int guess, AVExifEntry *entry)
{
int ret = 0, makernote_offset = -1, tell, is_ifd, count;
enum AVTiffDataType type;
@@ -536,7 +540,7 @@ 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, entries_left,
+ ret = exif_parse_ifd_list(logctx, gb, le, depth + 1, entries_left, bytes_left,
&entry->value.ifd, guess || entry->id == MAKERNOTE_TAG);
if (ret < 0 && entry->id == MAKERNOTE_TAG) {
/*
@@ -554,7 +558,7 @@ static int exif_decode_tag(void *logctx, GetByteContext *gb, int le,
entry->type = type == AV_TIFF_IFD ? AV_TIFF_UNDEFINED : type;
entry->count = count;
bytestream2_seek(gb, count * exif_sizes[type] > 4 ? payload : tell + 8, SEEK_SET);
- ret = exif_read_values(logctx, gb, le, entry);
+ ret = exif_read_values(logctx, gb, le, &bytes_left[guess], entry);
}
end:
@@ -564,7 +568,7 @@ end:
}
static int exif_parse_ifd_list(void *logctx, GetByteContext *gb, int le,
- int depth, int *entries_left,
+ int depth, int *entries_left, size_t *bytes_left,
AVExifMetadata *ifd, int guess)
{
uint32_t entries;
@@ -629,7 +633,8 @@ 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, entries_left, guess, &ifd->entries[i]);
+ ret = exif_decode_tag(logctx, gb, le, depth, entries_left, bytes_left,
+ guess, &ifd->entries[i]);
if (ret < 0)
goto end;
}
@@ -946,7 +951,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;
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);
+ size_t bytes_left[2] = { size, size };
+ ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, entries_left, bytes_left, ifd, 0);
if (ret < 0) {
av_log(logctx, AV_LOG_ERROR, "error decoding EXIF data: %s\n", av_err2str(ret));
return ret;
@@ -966,7 +972,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, entries_left, &extra_ifd, 1);
+ ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, entries_left, bytes_left, &extra_ifd, 1);
if (ret < 0) {
av_exif_free(&extra_ifd);
break;