diff --git a/ext/exif/exif.c b/ext/exif/exif.c index ba7f0304c691f..e367c576621ba 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -4307,76 +4307,74 @@ static int exif_isobmff_parse_box(unsigned char *buf, isobmff_box_type *box) static void exif_isobmff_parse_meta(unsigned char *data, unsigned char *end, isobmff_item_pos_type *pos) { isobmff_box_type box, item; - unsigned char *box_offset, *p, *p2; + unsigned char *box_offset, *p; int header_size, exif_id = -1, version, item_count, i; - for (box_offset = data + 4; box_offset + 16 < end; box_offset += box.size) { + size_t remain; +#define CHECK(n) do { \ + if (remain < n) { \ + return; \ + } \ +} while (0) +#define ADVANCE(n) do { \ + CHECK(n); \ + remain -= n; \ + p += n; \ +} while (0) + + for (box_offset = data + 4; box_offset < end - 16; box_offset += box.size) { header_size = exif_isobmff_parse_box(box_offset, &box); + p = box_offset; + remain = end - p; + if (box.type == FOURCC("iinf")) { - p = box_offset + header_size; - if (p>= end) { - return; - } + ADVANCE(header_size); version = p[0]; - p += 4; + ADVANCE(4); if (version < 2) { - if (p + 2>= end) { - return; - } - item_count = php_ifd_get16u(p, 1); - p += 2; + ADVANCE(2); + item_count = php_ifd_get16u(p - 2, 1); } else { - if (p + 4>= end) { - return; - } - item_count = php_ifd_get32u(p, 1); - p += 4; + ADVANCE(4); + item_count = php_ifd_get32u(p - 4, 1); } - for (i = 0; i < item_count && p + 20 < end; i++) { + for (i = 0; i < item_count && p < end - 20; i++) { header_size = exif_isobmff_parse_box(p, &item); - if (p + header_size + 12>= end) { - return; - } + CHECK(header_size + 12); if (!memcmp(p + header_size + 8, "Exif", 4)) { exif_id = php_ifd_get16u(p + header_size + 4, 1); break; } - p += item.size; + ADVANCE(item.size); } if (exif_id < 0) { break; } } else if (box.type == FOURCC("iloc")) { - p = box_offset + header_size; - if (p>= end) { - return; - } + ADVANCE(header_size); version = p[0]; - p += 6; + ADVANCE(6); if (version < 2) { - if (p + 2>= end) { - return; - } - item_count = php_ifd_get16u(p, 1); - p += 2; + ADVANCE(2); + item_count = php_ifd_get16u(p - 2, 1); } else { - if (p + 4>= end) { - return; - } - item_count = php_ifd_get32u(p, 1); - p += 4; + ADVANCE(4); + item_count = php_ifd_get32u(p - 4, 1); } - for (i = 0, p2 = p; i < item_count && p + 16 < end; i++, p2 += 16) { - if (php_ifd_get16u(p2, 1) == exif_id) { - pos->offset = php_ifd_get32u(p2 + 8, 1); - pos->size = php_ifd_get32u(p2 + 12, 1); + for (i = 0; i < item_count && p < end - 16; i++, p += 16) { + if (php_ifd_get16u(p, 1) == exif_id) { + pos->offset = php_ifd_get32u(p + 8, 1); + pos->size = php_ifd_get32u(p + 12, 1); break; } } break; } } + +#undef ADVANCE +#undef CHECK } static bool exif_scan_HEIF_header(image_info_type *ImageInfo, unsigned char *buf) @@ -4390,7 +4388,7 @@ static bool exif_scan_HEIF_header(image_info_type *ImageInfo, unsigned char *buf bool ret = false; pos.size = 0; - for (offset = php_ifd_get32u(buf, 1); ImageInfo->FileSize> offset + 16; offset += box.size) { + for (offset = php_ifd_get32u(buf, 1); ImageInfo->FileSize - 16> offset; offset += box.size) { if ((php_stream_seek(ImageInfo->infile, offset, SEEK_SET) < 0) || (exif_read_from_stream_file_looped(ImageInfo->infile, (char*)buf, 16) != 16)) { break; @@ -4410,7 +4408,8 @@ static bool exif_scan_HEIF_header(image_info_type *ImageInfo, unsigned char *buf exif_isobmff_parse_meta(data, data + limit, &pos); } if ((pos.size) && - (ImageInfo->FileSize>= pos.offset + pos.size) && + (pos.size < ImageInfo->FileSize) && + (ImageInfo->FileSize - pos.size>= pos.offset) && (php_stream_seek(ImageInfo->infile, pos.offset + 2, SEEK_SET)>= 0)) { if (limit>= pos.size - 2) { limit = pos.size - 2; @@ -4427,6 +4426,9 @@ static bool exif_scan_HEIF_header(image_info_type *ImageInfo, unsigned char *buf efree(data); break; } + if (offset + box.size < offset) { + break; + } } return ret; @@ -4497,7 +4499,7 @@ static bool exif_scan_FILE_header(image_info_type *ImageInfo) exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid TIFF file"); return false; } - } else if ((ImageInfo->FileSize> 12) && + } else if ((ImageInfo->FileSize> 16) && (!memcmp(file_header + 4, "ftyp", 4)) && (exif_read_from_stream_file_looped(ImageInfo->infile, (char*)(file_header + 8), 4) == 4) && ((!memcmp(file_header + 8, "heic", 4)) || (!memcmp(file_header + 8, "heix", 4)) || (!memcmp(file_header + 8, "mif1", 4)))) { diff --git a/ext/exif/tests/heic_box_overflow.phpt b/ext/exif/tests/heic_box_overflow.phpt new file mode 100644 index 0000000000000..b3a5e42df91fc --- /dev/null +++ b/ext/exif/tests/heic_box_overflow.phpt @@ -0,0 +1,27 @@ +--TEST-- +HEIC box overflow +--EXTENSIONS-- +exif +--FILE-- + +--CLEAN-- + +--EXPECTF-- +Warning: exif_read_data(heic_box_overflow): Invalid HEIF file in %s on line %d +bool(false)