diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 0edc8cadcdb..9db4a2831bb 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -64,7 +64,8 @@ typedef unsigned char uchar; #define EFREE_IF(ptr) if (ptr) efree(ptr) -#define MAX_IFD_NESTING_LEVEL 200 +#define MAX_IFD_NESTING_LEVEL 10 +#define MAX_IFD_TAGS 1000 /* {{{ arginfo */ ZEND_BEGIN_ARG_INFO(arginfo_exif_tagname, 0) @@ -2022,6 +2023,7 @@ typedef struct { int read_thumbnail; int read_all; int ifd_nesting_level; + int ifd_count; int num_errors; /* internal */ file_section_list file; @@ -2712,6 +2714,7 @@ static void exif_process_SOFn (uchar *Data, int marker, jpeg_sof_info *result) /* forward declarations */ static int exif_process_IFD_in_JPEG(image_info_type *ImageInfo, char *dir_start, char *offset_base, size_t IFDlength, size_t displacement, int section_index, int tag); static int exif_process_IFD_TAG( image_info_type *ImageInfo, char *dir_entry, char *offset_base, size_t IFDlength, size_t displacement, int section_index, int ReadNextIFD, tag_table_type tag_table); +static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offset, int section_index); /* {{{ exif_get_markername Get name of marker */ @@ -3283,7 +3286,7 @@ static int exif_process_IFD_in_MAKERNOTE(image_info_type *ImageInfo, char * valu /* {{{ exif_process_IFD_TAG * Process one of the nested IFDs directories. */ -static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, char *offset_base, size_t IFDlength, size_t displacement, int section_index, int ReadNextIFD, tag_table_type tag_table) +static int exif_process_IFD_TAG_impl(image_info_type *ImageInfo, char *dir_entry, char *offset_base, size_t IFDlength, size_t displacement, int section_index, int ReadNextIFD, tag_table_type tag_table) { size_t length; unsigned int tag, format, components; @@ -3296,13 +3299,6 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha int dump_free; #endif /* EXIF_DEBUG */ - /* Protect against corrupt headers */ - if (ImageInfo->ifd_nesting_level > MAX_IFD_NESTING_LEVEL) { - exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "corrupt EXIF header: maximum directory nesting level reached"); - return FALSE; - } - ImageInfo->ifd_nesting_level++; - tag = php_ifd_get16u(dir_entry, ImageInfo->motorola_intel); format = php_ifd_get16u(dir_entry+2, ImageInfo->motorola_intel); components = php_ifd_get32u(dir_entry+4, ImageInfo->motorola_intel); @@ -3622,6 +3618,24 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha } /* }}} */ +static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, char *offset_base, size_t IFDlength, size_t displacement, int section_index, int ReadNextIFD, tag_table_type tag_table) +{ + int result; + /* Protect against corrupt headers */ + if (ImageInfo->ifd_count++ > MAX_IFD_TAGS) { + exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "corrupt EXIF header: maximum IFD tag count reached"); + return FALSE; + } + if (ImageInfo->ifd_nesting_level > MAX_IFD_NESTING_LEVEL) { + exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "corrupt EXIF header: maximum directory nesting level reached"); + return FALSE; + } + ImageInfo->ifd_nesting_level++; + result = exif_process_IFD_TAG_impl(ImageInfo, dir_entry, offset_base, IFDlength, displacement, section_index, ReadNextIFD, tag_table); + ImageInfo->ifd_nesting_level--; + return result; +} + /* {{{ exif_process_IFD_in_JPEG * Process one of the nested IFDs directories. */ static int exif_process_IFD_in_JPEG(image_info_type *ImageInfo, char *dir_start, char *offset_base, size_t IFDlength, size_t displacement, int section_index, int tag) @@ -4055,7 +4069,7 @@ static int exif_scan_thumbnail(image_info_type *ImageInfo) /* {{{ exif_process_IFD_in_TIFF * Parse the TIFF header; */ -static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offset, int section_index) +static int exif_process_IFD_in_TIFF_impl(image_info_type *ImageInfo, size_t dir_offset, int section_index) { int i, sn, num_entries, sub_section_index = 0; unsigned char *dir_entry; @@ -4063,10 +4077,6 @@ static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offse int entry_tag , entry_type; tag_table_type tag_table = exif_get_tag_table(section_index); - if (ImageInfo->ifd_nesting_level > MAX_IFD_NESTING_LEVEL) { - return FALSE; - } - if (ImageInfo->FileSize >= 2 && ImageInfo->FileSize - 2 >= dir_offset) { sn = exif_file_sections_add(ImageInfo, M_PSEUDO, 2, NULL); #ifdef EXIF_DEBUG @@ -4210,7 +4220,6 @@ static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offse #ifdef EXIF_DEBUG exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Next IFD: %s @x%04X", exif_get_sectionname(sub_section_index), entry_offset); #endif - ImageInfo->ifd_nesting_level++; exif_process_IFD_in_TIFF(ImageInfo, entry_offset, sub_section_index); if (section_index!=SECTION_THUMBNAIL && entry_tag==TAG_SUB_IFD) { if (ImageInfo->Thumbnail.filetype != IMAGE_FILETYPE_UNKNOWN @@ -4254,7 +4263,6 @@ static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offse #ifdef EXIF_DEBUG exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read next IFD (THUMBNAIL) at x%04X", next_offset); #endif - ImageInfo->ifd_nesting_level++; exif_process_IFD_in_TIFF(ImageInfo, next_offset, SECTION_THUMBNAIL); #ifdef EXIF_DEBUG exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "%s THUMBNAIL @0x%04X + 0x%04X", ImageInfo->Thumbnail.data ? "Ignore" : "Read", ImageInfo->Thumbnail.offset, ImageInfo->Thumbnail.size); @@ -4291,6 +4299,21 @@ static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offse } /* }}} */ +static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offset, int section_index) +{ + int result; + if (ImageInfo->ifd_count++ > MAX_IFD_TAGS) { + return FALSE; + } + if (ImageInfo->ifd_nesting_level > MAX_IFD_NESTING_LEVEL) { + return FALSE; + } + ImageInfo->ifd_nesting_level++; + result = exif_process_IFD_in_TIFF_impl(ImageInfo, dir_offset, section_index); + ImageInfo->ifd_nesting_level--; + return result; +} + /* {{{ exif_scan_FILE_header * Parse the marker stream until SOS or EOI is seen; */ static int exif_scan_FILE_header(image_info_type *ImageInfo) @@ -4446,6 +4469,7 @@ static int exif_read_from_impl(image_info_type *ImageInfo, php_stream *stream, i ImageInfo->ifd_nesting_level = 0; + ImageInfo->ifd_count = 0; ImageInfo->num_errors = 0; /* Scan the headers */ diff --git a/ext/exif/tests/nesting_level_oom.phpt b/ext/exif/tests/nesting_level_oom.phpt new file mode 100644 index 00000000000..bde1f8f9543 --- /dev/null +++ b/ext/exif/tests/nesting_level_oom.phpt @@ -0,0 +1,10 @@ +--TEST-- +Should not cause OOM +--FILE-- + +--EXPECT-- +bool(false) diff --git a/ext/exif/tests/nesting_level_oom.tiff b/ext/exif/tests/nesting_level_oom.tiff new file mode 100644 index 00000000000..9bdcdc00cb4 Binary files /dev/null and b/ext/exif/tests/nesting_level_oom.tiff differ