5050/* needed for ssize_t definition */
5151#include <sys/types.h>
5252
53+ #ifdef __SANITIZE_ADDRESS__
54+ # include <sanitizer/asan_interface.h>
55+ #endif
56+
5357typedef unsigned char uchar ;
5458
5559#ifndef TRUE
@@ -2018,6 +2022,63 @@ typedef struct {
20182022} jpeg_sof_info ;
20192023/* }}} */
20202024
2025+ /* Base address for offset references, together with valid memory range.
2026+ * The valid range does not necessarily include the offset base. */
2027+ typedef struct {
2028+ char * offset_base ;
2029+ char * valid_start ; /* inclusive */
2030+ char * valid_end ; /* exclusive */
2031+ } exif_offset_info ;
2032+
2033+ static zend_always_inline zend_bool ptr_offset_overflows (char * ptr , size_t offset ) {
2034+ return UINTPTR_MAX - (uintptr_t ) ptr < offset ;
2035+ }
2036+
2037+ static inline void exif_offset_info_init (
2038+ exif_offset_info * info , char * offset_base , char * valid_start , size_t valid_length ) {
2039+ ZEND_ASSERT (!ptr_offset_overflows (valid_start , valid_length ));
2040+ #ifdef __SANITIZE_ADDRESS__
2041+ ZEND_ASSERT (!__asan_region_is_poisoned (valid_start , valid_length ));
2042+ #endif
2043+ info -> offset_base = offset_base ;
2044+ info -> valid_start = valid_start ;
2045+ info -> valid_end = valid_start + valid_length ;
2046+ }
2047+
2048+ /* Try to get a pointer at offset_base+offset with length dereferenceable bytes. */
2049+ static inline char * exif_offset_info_try_get (
2050+ const exif_offset_info * info , size_t offset , size_t length ) {
2051+ char * start , * end ;
2052+ if (ptr_offset_overflows (info -> offset_base , offset )) {
2053+ return NULL ;
2054+ }
2055+
2056+ start = info -> offset_base + offset ;
2057+ if (ptr_offset_overflows (start , length )) {
2058+ return NULL ;
2059+ }
2060+
2061+ end = start + length ;
2062+ if (start < info -> valid_start || end > info -> valid_end ) {
2063+ return NULL ;
2064+ }
2065+
2066+ return start ;
2067+ }
2068+
2069+ static inline zend_bool exif_offset_info_contains (
2070+ const exif_offset_info * info , char * start , size_t length ) {
2071+ char * end ;
2072+ if (ptr_offset_overflows (start , length )) {
2073+ return 0 ;
2074+ }
2075+
2076+ /* start and valid_start are both inclusive, end and valid_end are both exclusive,
2077+ * so we use >= and <= to do the checks, respectively. */
2078+ end = start + length ;
2079+ return start >= info -> valid_start && end <= info -> valid_end ;
2080+ }
2081+
20212082/* {{{ exif_file_sections_add
20222083 Add a file_section to image_info
20232084 returns the used block or -1. if size>0 and data == NULL buffer of size is allocated
@@ -2624,8 +2685,8 @@ static void exif_process_SOFn (uchar *Data, int marker, jpeg_sof_info *result)
26242685/* }}} */
26252686
26262687/* forward declarations */
2627- 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 );
2628- 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 );
2688+ static int exif_process_IFD_in_JPEG (image_info_type * ImageInfo , char * dir_start , const exif_offset_info * info , size_t displacement , int section_index , int tag );
2689+ static int exif_process_IFD_TAG (image_info_type * ImageInfo , char * dir_entry , const exif_offset_info * info , size_t displacement , int section_index , int ReadNextIFD , tag_table_type tag_table );
26292690
26302691/* {{{ exif_get_markername
26312692 Get name of marker */
@@ -2871,7 +2932,7 @@ static void exif_thumbnail_build(image_info_type *ImageInfo) {
28712932
28722933/* {{{ exif_thumbnail_extract
28732934 * Grab the thumbnail, corrected */
2874- static void exif_thumbnail_extract (image_info_type * ImageInfo , char * offset , size_t length ) {
2935+ static void exif_thumbnail_extract (image_info_type * ImageInfo , const exif_offset_info * info ) {
28752936 if (ImageInfo -> Thumbnail .data ) {
28762937 exif_error_docref ("exif_read_data#error_mult_thumb" EXIFERR_CC , ImageInfo , E_WARNING , "Multiple possible thumbnails" );
28772938 return ; /* Should not happen */
@@ -2888,14 +2949,13 @@ static void exif_thumbnail_extract(image_info_type *ImageInfo, char *offset, siz
28882949 return ;
28892950 }
28902951 /* Check to make sure we are not going to go past the ExifLength */
2891- if (ImageInfo -> Thumbnail .size > length
2892- || (ImageInfo -> Thumbnail .offset + ImageInfo -> Thumbnail .size ) > length
2893- || ImageInfo -> Thumbnail .offset > length - ImageInfo -> Thumbnail .size
2894- ) {
2952+ char * thumbnail = exif_offset_info_try_get (
2953+ info , ImageInfo -> Thumbnail .offset , ImageInfo -> Thumbnail .size );
2954+ if (!thumbnail ) {
28952955 EXIF_ERRLOG_THUMBEOF (ImageInfo )
28962956 return ;
28972957 }
2898- ImageInfo -> Thumbnail .data = estrndup (offset + ImageInfo -> Thumbnail . offset , ImageInfo -> Thumbnail .size );
2958+ ImageInfo -> Thumbnail .data = estrndup (thumbnail , ImageInfo -> Thumbnail .size );
28992959 exif_thumbnail_build (ImageInfo );
29002960}
29012961/* }}} */
@@ -3063,14 +3123,14 @@ static int exif_process_unicode(image_info_type *ImageInfo, xp_field_type *xp_fi
30633123
30643124/* {{{ exif_process_IFD_in_MAKERNOTE
30653125 * Process nested IFDs directories in Maker Note. */
3066- static int exif_process_IFD_in_MAKERNOTE (image_info_type * ImageInfo , char * value_ptr , int value_len , char * offset_base , size_t IFDlength , size_t displacement )
3126+ static int exif_process_IFD_in_MAKERNOTE (image_info_type * ImageInfo , char * value_ptr , int value_len , const exif_offset_info * info , size_t displacement )
30673127{
30683128 size_t i ;
30693129 int de , section_index = SECTION_MAKERNOTE ;
30703130 int NumDirEntries , old_motorola_intel ;
30713131 const maker_note_type * maker_note ;
30723132 char * dir_start ;
3073- int data_len ;
3133+ exif_offset_info new_info ;
30743134
30753135 for (i = 0 ; i <=sizeof (maker_note_array )/sizeof (maker_note_type ); i ++ ) {
30763136 if (i == sizeof (maker_note_array )/sizeof (maker_note_type )) {
@@ -3122,12 +3182,11 @@ static int exif_process_IFD_in_MAKERNOTE(image_info_type *ImageInfo, char * valu
31223182
31233183 switch (maker_note -> offset_mode ) {
31243184 case MN_OFFSET_MAKER :
3125- offset_base = value_ptr ;
3126- data_len = value_len ;
3185+ exif_offset_info_init ( & new_info , value_ptr , value_ptr , value_len ) ;
3186+ info = & new_info ;
31273187 break ;
31283188 default :
31293189 case MN_OFFSET_NORMAL :
3130- data_len = value_len ;
31313190 break ;
31323191 }
31333192
@@ -3143,7 +3202,7 @@ static int exif_process_IFD_in_MAKERNOTE(image_info_type *ImageInfo, char * valu
31433202 for (de = 0 ;de < NumDirEntries ;de ++ ) {
31443203 size_t offset = 2 + 12 * de ;
31453204 if (!exif_process_IFD_TAG (ImageInfo , dir_start + offset ,
3146- offset_base , data_len - offset , displacement , section_index , 0 , maker_note -> tag_table )) {
3205+ info , displacement , section_index , 0 , maker_note -> tag_table )) {
31473206 return FALSE;
31483207 }
31493208 }
@@ -3166,7 +3225,7 @@ static int exif_process_IFD_in_MAKERNOTE(image_info_type *ImageInfo, char * valu
31663225
31673226/* {{{ exif_process_IFD_TAG
31683227 * Process one of the nested IFDs directories. */
3169- 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 )
3228+ static int exif_process_IFD_TAG (image_info_type * ImageInfo , char * dir_entry , const exif_offset_info * info , size_t displacement , int section_index , int ReadNextIFD , tag_table_type tag_table )
31703229{
31713230 size_t length ;
31723231 unsigned int tag , format , components ;
@@ -3206,28 +3265,16 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha
32063265 byte_count = (size_t )byte_count_signed ;
32073266
32083267 if (byte_count > 4 ) {
3209- offset_val = php_ifd_get32u (dir_entry + 8 , ImageInfo -> motorola_intel );
32103268 /* If its bigger than 4 bytes, the dir entry contains an offset. */
3211- value_ptr = offset_base + offset_val ;
3212- /*
3213- dir_entry is ImageInfo->file.list[sn].data+2+i*12
3214- offset_base is ImageInfo->file.list[sn].data-dir_offset
3215- dir_entry - offset_base is dir_offset+2+i*12
3216- */
3217- if (byte_count > IFDlength || offset_val > IFDlength - byte_count || value_ptr < dir_entry || offset_val < (size_t )(dir_entry - offset_base ) || dir_entry <= offset_base ) {
3269+ offset_val = php_ifd_get32u (dir_entry + 8 , ImageInfo -> motorola_intel );
3270+ value_ptr = exif_offset_info_try_get (info , offset_val , byte_count );
3271+ if (!value_ptr ) {
32183272 /* It is important to check for IMAGE_FILETYPE_TIFF
32193273 * JPEG does not use absolute pointers instead its pointers are
32203274 * relative to the start of the TIFF header in APP1 section. */
3275+ // TODO: Shouldn't we also be taking "displacement" into account here?
32213276 if (byte_count > ImageInfo -> FileSize || offset_val > ImageInfo -> FileSize - byte_count || (ImageInfo -> FileType != IMAGE_FILETYPE_TIFF_II && ImageInfo -> FileType != IMAGE_FILETYPE_TIFF_MM && ImageInfo -> FileType != IMAGE_FILETYPE_JPEG )) {
3222- if (value_ptr < dir_entry ) {
3223- /* we can read this if offset_val > 0 */
3224- /* some files have their values in other parts of the file */
3225- exif_error_docref ("exif_read_data#error_ifd" EXIFERR_CC , ImageInfo , E_WARNING , "Process tag(x%04X=%s): Illegal pointer offset(x%04X < x%04X)" , tag , exif_get_tagname_debug (tag , tag_table ), offset_val , dir_entry );
3226- } else {
3227- /* this is for sure not allowed */
3228- /* exception are IFD pointers */
3229- exif_error_docref ("exif_read_data#error_ifd" EXIFERR_CC , ImageInfo , E_WARNING , "Process tag(x%04X=%s): Illegal pointer offset(x%04X + x%04X = x%04X > x%04X)" , tag , exif_get_tagname_debug (tag , tag_table ), offset_val , byte_count , offset_val + byte_count , IFDlength );
3230- }
3277+ exif_error_docref ("exif_read_data#error_ifd" EXIFERR_CC , ImageInfo , E_WARNING , "Process tag(x%04X=%s): Illegal pointer offset(x%04X + x%04X = x%04X > x%04X)" , tag , exif_get_tagname_debug (tag , tag_table ), offset_val , byte_count , offset_val + byte_count , ImageInfo -> FileSize );
32313278 return FALSE;
32323279 }
32333280 if (byte_count > sizeof (cbuf )) {
@@ -3263,7 +3310,8 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha
32633310 } else {
32643311 /* 4 bytes or less and value is in the dir entry itself */
32653312 value_ptr = dir_entry + 8 ;
3266- offset_val = value_ptr - offset_base ;
3313+ // TODO: This is dubious, but the value is only used for debugging.
3314+ offset_val = value_ptr - info -> offset_base ;
32673315 }
32683316
32693317 ImageInfo -> sections_found |= FOUND_ANY_TAG ;
@@ -3446,7 +3494,7 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha
34463494 break ;
34473495
34483496 case TAG_MAKER_NOTE :
3449- if (!exif_process_IFD_in_MAKERNOTE (ImageInfo , value_ptr , byte_count , offset_base , IFDlength , displacement )) {
3497+ if (!exif_process_IFD_in_MAKERNOTE (ImageInfo , value_ptr , byte_count , info , displacement )) {
34503498 EFREE_IF (outside );
34513499 return FALSE;
34523500 }
@@ -3482,13 +3530,14 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha
34823530 sub_section_index = SECTION_INTEROP ;
34833531 break ;
34843532 }
3485- Subdir_start = offset_base + php_ifd_get32u (value_ptr , ImageInfo -> motorola_intel );
3486- if (Subdir_start < offset_base || Subdir_start > offset_base + IFDlength ) {
3533+ offset_val = php_ifd_get32u (value_ptr , ImageInfo -> motorola_intel );
3534+ Subdir_start = exif_offset_info_try_get (info , offset_val , 0 );
3535+ if (!Subdir_start ) {
34873536 exif_error_docref ("exif_read_data#error_ifd" EXIFERR_CC , ImageInfo , E_WARNING , "Illegal IFD Pointer" );
34883537 EFREE_IF (outside );
34893538 return FALSE;
34903539 }
3491- if (!exif_process_IFD_in_JPEG (ImageInfo , Subdir_start , offset_base , IFDlength , displacement , sub_section_index , tag )) {
3540+ if (!exif_process_IFD_in_JPEG (ImageInfo , Subdir_start , info , displacement , sub_section_index , tag )) {
34923541 EFREE_IF (outside );
34933542 return FALSE;
34943543 }
@@ -3506,7 +3555,7 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha
35063555
35073556/* {{{ exif_process_IFD_in_JPEG
35083557 * Process one of the nested IFDs directories. */
3509- 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 )
3558+ static int exif_process_IFD_in_JPEG (image_info_type * ImageInfo , char * dir_start , const exif_offset_info * info , size_t displacement , int section_index , int tag )
35103559{
35113560 int de ;
35123561 int NumDirEntries ;
@@ -3518,21 +3567,21 @@ static int exif_process_IFD_in_JPEG(image_info_type *ImageInfo, char *dir_start,
35183567
35193568 ImageInfo -> sections_found |= FOUND_IFD0 ;
35203569
3521- if (( dir_start + 2 ) > ( offset_base + IFDlength )) {
3570+ if (! exif_offset_info_contains ( info , dir_start , 2 )) {
35223571 exif_error_docref ("exif_read_data#error_ifd" EXIFERR_CC , ImageInfo , E_WARNING , "Illegal IFD size" );
35233572 return FALSE;
35243573 }
35253574
35263575 NumDirEntries = php_ifd_get16u (dir_start , ImageInfo -> motorola_intel );
35273576
3528- if (( dir_start + 2 + NumDirEntries * 12 ) > ( offset_base + IFDlength )) {
3529- exif_error_docref ("exif_read_data#error_ifd" EXIFERR_CC , ImageInfo , E_WARNING , "Illegal IFD size: x%04X + 2 + x%04X*12 = x%04X > x%04X" , (int )((size_t )dir_start + 2 - (size_t )offset_base ), NumDirEntries , (int )((size_t )dir_start + 2 + NumDirEntries * 12 - (size_t )offset_base ), IFDlength );
3577+ if (! exif_offset_info_contains ( info , dir_start + 2 , NumDirEntries * 12 )) {
3578+ exif_error_docref ("exif_read_data#error_ifd" EXIFERR_CC , ImageInfo , E_WARNING , "Illegal IFD size: x%04X + 2 + x%04X*12 = x%04X > x%04X" , (int )((size_t )dir_start + 2 - (size_t )info -> valid_start ), NumDirEntries , (int )((size_t )dir_start + 2 + NumDirEntries * 12 - (size_t )info -> valid_start ), info -> valid_end - info -> valid_start );
35303579 return FALSE;
35313580 }
35323581
35333582 for (de = 0 ;de < NumDirEntries ;de ++ ) {
35343583 if (!exif_process_IFD_TAG (ImageInfo , dir_start + 2 + 12 * de ,
3535- offset_base , IFDlength , displacement , section_index , 1 , exif_get_tag_table (section_index ))) {
3584+ info , displacement , section_index , 1 , exif_get_tag_table (section_index ))) {
35363585 return FALSE;
35373586 }
35383587 }
@@ -3546,7 +3595,7 @@ static int exif_process_IFD_in_JPEG(image_info_type *ImageInfo, char *dir_start,
35463595 * Hack to make it process IDF1 I hope
35473596 * There are 2 IDFs, the second one holds the keys (0x0201 and 0x0202) to the thumbnail
35483597 */
3549- if (( dir_start + 2 + 12 * de + 4 ) > ( offset_base + IFDlength )) {
3598+ if (! exif_offset_info_contains ( info , dir_start + 2 + NumDirEntries * 12 , 4 )) {
35503599 exif_error_docref ("exif_read_data#error_ifd" EXIFERR_CC , ImageInfo , E_WARNING , "Illegal IFD size" );
35513600 return FALSE;
35523601 }
@@ -3556,16 +3605,16 @@ static int exif_process_IFD_in_JPEG(image_info_type *ImageInfo, char *dir_start,
35563605 }
35573606
35583607 if (NextDirOffset ) {
3559- /* the next line seems false but here IFDlength means length of all IFDs */
3560- if (offset_base + NextDirOffset < offset_base || offset_base + NextDirOffset > offset_base + IFDlength ) {
3608+ char * next_dir_start = exif_offset_info_try_get ( info , NextDirOffset , 0 );
3609+ if (! next_dir_start ) {
35613610 exif_error_docref ("exif_read_data#error_ifd" EXIFERR_CC , ImageInfo , E_WARNING , "Illegal IFD offset" );
35623611 return FALSE;
35633612 }
35643613 /* That is the IFD for the first thumbnail */
35653614#ifdef EXIF_DEBUG
35663615 exif_error_docref (NULL EXIFERR_CC , ImageInfo , E_NOTICE , "Expect next IFD to be thumbnail" );
35673616#endif
3568- if (exif_process_IFD_in_JPEG (ImageInfo , offset_base + NextDirOffset , offset_base , IFDlength , displacement , SECTION_THUMBNAIL , 0 )) {
3617+ if (exif_process_IFD_in_JPEG (ImageInfo , next_dir_start , info , displacement , SECTION_THUMBNAIL , 0 )) {
35693618#ifdef EXIF_DEBUG
35703619 exif_error_docref (NULL EXIFERR_CC , ImageInfo , E_NOTICE , "Thumbnail size: 0x%04X" , ImageInfo -> Thumbnail .size );
35713620#endif
@@ -3574,7 +3623,7 @@ static int exif_process_IFD_in_JPEG(image_info_type *ImageInfo, char *dir_start,
35743623 && ImageInfo -> Thumbnail .offset
35753624 && ImageInfo -> read_thumbnail
35763625 ) {
3577- exif_thumbnail_extract (ImageInfo , offset_base , IFDlength );
3626+ exif_thumbnail_extract (ImageInfo , info );
35783627 }
35793628 return TRUE;
35803629 } else {
@@ -3591,6 +3640,7 @@ static int exif_process_IFD_in_JPEG(image_info_type *ImageInfo, char *dir_start,
35913640static void exif_process_TIFF_in_JPEG (image_info_type * ImageInfo , char * CharBuf , size_t length , size_t displacement )
35923641{
35933642 unsigned exif_value_2a , offset_of_ifd ;
3643+ exif_offset_info info ;
35943644
35953645 /* set the thumbnail stuff to nothing so we can test to see if they get set up */
35963646 if (memcmp (CharBuf , "II" , 2 ) == 0 ) {
@@ -3620,7 +3670,8 @@ static void exif_process_TIFF_in_JPEG(image_info_type *ImageInfo, char *CharBuf,
36203670
36213671 ImageInfo -> sections_found |= FOUND_IFD0 ;
36223672 /* First directory starts at offset 8. Offsets starts at 0. */
3623- exif_process_IFD_in_JPEG (ImageInfo , CharBuf + offset_of_ifd , CharBuf , length /*-14*/ , displacement , SECTION_IFD0 , 0 );
3673+ exif_offset_info_init (& info , CharBuf , CharBuf , length /*-14*/ );
3674+ exif_process_IFD_in_JPEG (ImageInfo , CharBuf + offset_of_ifd , & info , displacement , SECTION_IFD0 , 0 );
36243675
36253676#ifdef EXIF_DEBUG
36263677 exif_error_docref (NULL EXIFERR_CC , ImageInfo , E_NOTICE , "Process TIFF in JPEG done ");
@@ -4116,9 +4167,12 @@ static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offse
41164167 exif_error_docref (NULL EXIFERR_CC , ImageInfo , E_NOTICE , "Next IFD: %s done" , exif_get_sectionname (sub_section_index ));
41174168#endif
41184169 } else {
4119- if (!exif_process_IFD_TAG (ImageInfo , (char * )dir_entry ,
4120- (char * )(ImageInfo -> file .list [sn ].data - dir_offset ),
4121- ifd_size , 0 , section_index , 0 , tag_table )) {
4170+ exif_offset_info info ;
4171+ exif_offset_info_init (& info ,
4172+ (char * ) (ImageInfo -> file .list [sn ].data - dir_offset ),
4173+ (char * ) ImageInfo -> file .list [sn ].data , ifd_size );
4174+ if (!exif_process_IFD_TAG (ImageInfo , (char * )dir_entry , & info ,
4175+ 0 , section_index , 0 , tag_table )) {
41224176 return FALSE;
41234177 }
41244178 }
0 commit comments