Skip to content

Commit 376bbbd

Browse files
committed
Make MAX_IFD_NESTING_LEVEL an actual nesting level
Currently we only ever increment ifd_nesting_level, so this ends up being a limit on the total number of IFD tags and we regularly get bug reports of it being exceeded. I think the intention behind this limit was to prevent recursion stack overflow, and for that we only need to check actual recursive usage. I've implemented that here, and dropped the nesting limit down to a smaller value (which still passes our tests). However, it seems that we do also need to have a total limit on the number of tags, as we don't catch some instances of infinite looping otherwise. Add this as a separate limit with a higher value, that should hopefully be sufficient. This is expected to fix a number of bugs: https://bugs.php.net/bug.php?id=78083 https://bugs.php.net/bug.php?id=78701 https://bugs.php.net/bug.php?id=79907 https://bugs.php.net/bug.php?id=80016
1 parent e948188 commit 376bbbd

File tree

3 files changed

+50
-16
lines changed

3 files changed

+50
-16
lines changed

ext/exif/exif.c

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ typedef unsigned char uchar;
6666

6767
#define EFREE_IF(ptr) if (ptr) efree(ptr)
6868

69-
#define MAX_IFD_NESTING_LEVEL 200
69+
#define MAX_IFD_NESTING_LEVEL 10
70+
#define MAX_IFD_TAGS 1000
7071

7172
/* {{{ arginfo */
7273
ZEND_BEGIN_ARG_INFO(arginfo_exif_tagname, 0)
@@ -1940,6 +1941,7 @@ typedef struct {
19401941
int read_thumbnail;
19411942
int read_all;
19421943
int ifd_nesting_level;
1944+
int ifd_count;
19431945
/* internal */
19441946
file_section_list file;
19451947
} image_info_type;
@@ -2674,6 +2676,7 @@ static void exif_process_SOFn (uchar *Data, int marker, jpeg_sof_info *result)
26742676
/* forward declarations */
26752677
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);
26762678
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);
2679+
static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offset, int section_index);
26772680

26782681
/* {{{ exif_get_markername
26792682
Get name of marker */
@@ -3246,7 +3249,7 @@ static int exif_process_IFD_in_MAKERNOTE(image_info_type *ImageInfo, char * valu
32463249

32473250
/* {{{ exif_process_IFD_TAG
32483251
* Process one of the nested IFDs directories. */
3249-
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)
3252+
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)
32503253
{
32513254
size_t length;
32523255
unsigned int tag, format, components;
@@ -3259,13 +3262,6 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha
32593262
int dump_free;
32603263
#endif /* EXIF_DEBUG */
32613264

3262-
/* Protect against corrupt headers */
3263-
if (ImageInfo->ifd_nesting_level > MAX_IFD_NESTING_LEVEL) {
3264-
exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "corrupt EXIF header: maximum directory nesting level reached");
3265-
return FALSE;
3266-
}
3267-
ImageInfo->ifd_nesting_level++;
3268-
32693265
tag = php_ifd_get16u(dir_entry, ImageInfo->motorola_intel);
32703266
format = php_ifd_get16u(dir_entry+2, ImageInfo->motorola_intel);
32713267
components = php_ifd_get32u(dir_entry+4, ImageInfo->motorola_intel);
@@ -3585,6 +3581,24 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha
35853581
}
35863582
/* }}} */
35873583

3584+
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)
3585+
{
3586+
int result;
3587+
/* Protect against corrupt headers */
3588+
if (ImageInfo->ifd_count++ > MAX_IFD_TAGS) {
3589+
exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "corrupt EXIF header: maximum IFD tag count reached");
3590+
return FALSE;
3591+
}
3592+
if (ImageInfo->ifd_nesting_level > MAX_IFD_NESTING_LEVEL) {
3593+
exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "corrupt EXIF header: maximum directory nesting level reached");
3594+
return FALSE;
3595+
}
3596+
ImageInfo->ifd_nesting_level++;
3597+
result = exif_process_IFD_TAG_impl(ImageInfo, dir_entry, offset_base, IFDlength, displacement, section_index, ReadNextIFD, tag_table);
3598+
ImageInfo->ifd_nesting_level--;
3599+
return result;
3600+
}
3601+
35883602
/* {{{ exif_process_IFD_in_JPEG
35893603
* Process one of the nested IFDs directories. */
35903604
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)
@@ -4018,7 +4032,7 @@ static int exif_scan_thumbnail(image_info_type *ImageInfo)
40184032

40194033
/* {{{ exif_process_IFD_in_TIFF
40204034
* Parse the TIFF header; */
4021-
static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offset, int section_index)
4035+
static int exif_process_IFD_in_TIFF_impl(image_info_type *ImageInfo, size_t dir_offset, int section_index)
40224036
{
40234037
int i, sn, num_entries, sub_section_index = 0;
40244038
unsigned char *dir_entry;
@@ -4027,10 +4041,6 @@ static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offse
40274041
int entry_tag , entry_type;
40284042
tag_table_type tag_table = exif_get_tag_table(section_index);
40294043

4030-
if (ImageInfo->ifd_nesting_level > MAX_IFD_NESTING_LEVEL) {
4031-
return FALSE;
4032-
}
4033-
40344044
if (ImageInfo->FileSize >= 2 && ImageInfo->FileSize - 2 >= dir_offset) {
40354045
sn = exif_file_sections_add(ImageInfo, M_PSEUDO, 2, NULL);
40364046
#ifdef EXIF_DEBUG
@@ -4174,7 +4184,6 @@ static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offse
41744184
#ifdef EXIF_DEBUG
41754185
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Next IFD: %s @x%04X", exif_get_sectionname(sub_section_index), entry_offset);
41764186
#endif
4177-
ImageInfo->ifd_nesting_level++;
41784187
exif_process_IFD_in_TIFF(ImageInfo, entry_offset, sub_section_index);
41794188
if (section_index!=SECTION_THUMBNAIL && entry_tag==TAG_SUB_IFD) {
41804189
if (ImageInfo->Thumbnail.filetype != IMAGE_FILETYPE_UNKNOWN
@@ -4218,7 +4227,6 @@ static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offse
42184227
#ifdef EXIF_DEBUG
42194228
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read next IFD (THUMBNAIL) at x%04X", next_offset);
42204229
#endif
4221-
ImageInfo->ifd_nesting_level++;
42224230
exif_process_IFD_in_TIFF(ImageInfo, next_offset, SECTION_THUMBNAIL);
42234231
#ifdef EXIF_DEBUG
42244232
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);
@@ -4255,6 +4263,21 @@ static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offse
42554263
}
42564264
/* }}} */
42574265

4266+
static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offset, int section_index)
4267+
{
4268+
int result;
4269+
if (ImageInfo->ifd_count++ > MAX_IFD_TAGS) {
4270+
return FALSE;
4271+
}
4272+
if (ImageInfo->ifd_nesting_level > MAX_IFD_NESTING_LEVEL) {
4273+
return FALSE;
4274+
}
4275+
ImageInfo->ifd_nesting_level++;
4276+
result = exif_process_IFD_in_TIFF_impl(ImageInfo, dir_offset, section_index);
4277+
ImageInfo->ifd_nesting_level--;
4278+
return result;
4279+
}
4280+
42584281
/* {{{ exif_scan_FILE_header
42594282
* Parse the marker stream until SOS or EOI is seen; */
42604283
static int exif_scan_FILE_header(image_info_type *ImageInfo)
@@ -4410,6 +4433,7 @@ static int exif_read_from_impl(image_info_type *ImageInfo, php_stream *stream, i
44104433

44114434

44124435
ImageInfo->ifd_nesting_level = 0;
4436+
ImageInfo->ifd_count = 0;
44134437

44144438
/* Scan the headers */
44154439
ret = exif_scan_FILE_header(ImageInfo);

ext/exif/tests/nesting_level_oom.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Should not cause OOM
3+
--FILE--
4+
<?php
5+
6+
var_dump(@exif_read_data(__DIR__ . '/nesting_level_oom.tiff'));
7+
8+
?>
9+
--EXPECT--
10+
bool(false)

ext/exif/tests/nesting_level_oom.tiff

438 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)