549 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			549 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 98648d3b62d4f123915410b8cae47954f9223000 Mon Sep 17 00:00:00 2001
 | |
| From: Even Rouault <even.rouault@spatialys.com>
 | |
| Date: Thu, 6 Oct 2022 10:11:06 +0000
 | |
| Subject: [PATCH] (CVE-2023-40090) Improved IFD-Loop Handling (fixes #455)
 | |
| 
 | |
| Closes #455
 | |
| 
 | |
| See merge request libtiff/libtiff!386
 | |
| 
 | |
| (cherry picked from commit d093eb5d961e21ba51420bc22382c514683a4d91)
 | |
| ---
 | |
|  libtiff/tif_close.c   |   6 +-
 | |
|  libtiff/tif_dir.c     | 129 +++++++++++++++++++++++++-----------
 | |
|  libtiff/tif_dir.h     |   2 +
 | |
|  libtiff/tif_dirread.c | 147 +++++++++++++++++++++++++++++++++---------
 | |
|  libtiff/tif_open.c    |   3 +-
 | |
|  libtiff/tiffiop.h     |   3 +-
 | |
|  6 files changed, 219 insertions(+), 71 deletions(-)
 | |
| 
 | |
| diff --git a/libtiff/tif_close.c b/libtiff/tif_close.c
 | |
| index 6c9f7349..b5fa9603 100644
 | |
| --- a/libtiff/tif_close.c
 | |
| +++ b/libtiff/tif_close.c
 | |
| @@ -52,8 +52,10 @@ TIFFCleanup(TIFF* tif)
 | |
|  	(*tif->tif_cleanup)(tif);
 | |
|  	TIFFFreeDirectory(tif);
 | |
|  
 | |
| -	if (tif->tif_dirlist)
 | |
| -		_TIFFfree(tif->tif_dirlist);
 | |
| +	if (tif->tif_dirlistoff)
 | |
| +		_TIFFfree(tif->tif_dirlistoff);
 | |
| +	if (tif->tif_dirlistdirn)
 | |
| +		_TIFFfree(tif->tif_dirlistdirn);
 | |
|  
 | |
|  	/*
 | |
|           * Clean up client info links.
 | |
| diff --git a/libtiff/tif_dir.c b/libtiff/tif_dir.c
 | |
| index a4295dc9..d9022f5b 100644
 | |
| --- a/libtiff/tif_dir.c
 | |
| +++ b/libtiff/tif_dir.c
 | |
| @@ -1521,12 +1521,22 @@ TIFFDefaultDirectory(TIFF* tif)
 | |
|  }
 | |
|  
 | |
|  static int
 | |
| -TIFFAdvanceDirectory(TIFF* tif, uint64_t* nextdir, uint64_t* off)
 | |
| +TIFFAdvanceDirectory(TIFF* tif, uint64_t* nextdiroff, uint64_t* off, uint16_t* nextdirnum)
 | |
|  {
 | |
|  	static const char module[] = "TIFFAdvanceDirectory";
 | |
| +
 | |
| +	/* Add this directory to the directory list, if not already in. */
 | |
| +	if (!_TIFFCheckDirNumberAndOffset(tif, *nextdirnum, *nextdiroff)) {
 | |
| +		TIFFErrorExt(tif->tif_clientdata, module, "Starting directory %"PRIu16" at offset 0x%"PRIx64" (%"PRIu64") might cause an IFD loop",
 | |
| +			*nextdirnum, *nextdiroff, *nextdiroff);
 | |
| +		*nextdiroff = 0;
 | |
| +		*nextdirnum = 0;
 | |
| +		return(0);
 | |
| +	}
 | |
| +
 | |
|  	if (isMapped(tif))
 | |
|  	{
 | |
| -		uint64_t poff=*nextdir;
 | |
| +		uint64_t poff=*nextdiroff;
 | |
|  		if (!(tif->tif_flags&TIFF_BIGTIFF))
 | |
|  		{
 | |
|  			tmsize_t poffa,poffb,poffc,poffd;
 | |
| @@ -1537,7 +1547,7 @@ TIFFAdvanceDirectory(TIFF* tif, uint64_t* nextdir, uint64_t* off)
 | |
|  			if (((uint64_t)poffa != poff) || (poffb < poffa) || (poffb < (tmsize_t)sizeof(uint16_t)) || (poffb > tif->tif_size))
 | |
|  			{
 | |
|  				TIFFErrorExt(tif->tif_clientdata,module,"Error fetching directory count");
 | |
| -                                  *nextdir=0;
 | |
| +                                  *nextdiroff=0;
 | |
|  				return(0);
 | |
|  			}
 | |
|  			_TIFFmemcpy(&dircount,tif->tif_base+poffa,sizeof(uint16_t));
 | |
| @@ -1555,7 +1565,7 @@ TIFFAdvanceDirectory(TIFF* tif, uint64_t* nextdir, uint64_t* off)
 | |
|  			_TIFFmemcpy(&nextdir32,tif->tif_base+poffc,sizeof(uint32_t));
 | |
|  			if (tif->tif_flags&TIFF_SWAB)
 | |
|  				TIFFSwabLong(&nextdir32);
 | |
| -			*nextdir=nextdir32;
 | |
| +			*nextdiroff=nextdir32;
 | |
|  		}
 | |
|  		else
 | |
|  		{
 | |
| @@ -1587,11 +1597,10 @@ TIFFAdvanceDirectory(TIFF* tif, uint64_t* nextdir, uint64_t* off)
 | |
|  			}
 | |
|  			if (off!=NULL)
 | |
|  				*off=(uint64_t)poffc;
 | |
| -			_TIFFmemcpy(nextdir,tif->tif_base+poffc,sizeof(uint64_t));
 | |
| +			_TIFFmemcpy(nextdiroff,tif->tif_base+poffc,sizeof(uint64_t));
 | |
|  			if (tif->tif_flags&TIFF_SWAB)
 | |
| -				TIFFSwabLong8(nextdir);
 | |
| +				TIFFSwabLong8(nextdiroff);
 | |
|  		}
 | |
| -		return(1);
 | |
|  	}
 | |
|  	else
 | |
|  	{
 | |
| @@ -1599,7 +1608,7 @@ TIFFAdvanceDirectory(TIFF* tif, uint64_t* nextdir, uint64_t* off)
 | |
|  		{
 | |
|  			uint16_t dircount;
 | |
|  			uint32_t nextdir32;
 | |
| -			if (!SeekOK(tif, *nextdir) ||
 | |
| +			if (!SeekOK(tif, *nextdiroff) ||
 | |
|  			    !ReadOK(tif, &dircount, sizeof (uint16_t))) {
 | |
|  				TIFFErrorExt(tif->tif_clientdata, module, "%s: Error fetching directory count",
 | |
|  				    tif->tif_name);
 | |
| @@ -1620,13 +1629,13 @@ TIFFAdvanceDirectory(TIFF* tif, uint64_t* nextdir, uint64_t* off)
 | |
|  			}
 | |
|  			if (tif->tif_flags & TIFF_SWAB)
 | |
|  				TIFFSwabLong(&nextdir32);
 | |
| -			*nextdir=nextdir32;
 | |
| +			*nextdiroff=nextdir32;
 | |
|  		}
 | |
|  		else
 | |
|  		{
 | |
|  			uint64_t dircount64;
 | |
|  			uint16_t dircount16;
 | |
| -			if (!SeekOK(tif, *nextdir) ||
 | |
| +			if (!SeekOK(tif, *nextdiroff) ||
 | |
|  			    !ReadOK(tif, &dircount64, sizeof (uint64_t))) {
 | |
|  				TIFFErrorExt(tif->tif_clientdata, module, "%s: Error fetching directory count",
 | |
|  				    tif->tif_name);
 | |
| @@ -1646,17 +1655,27 @@ TIFFAdvanceDirectory(TIFF* tif, uint64_t* nextdir, uint64_t* off)
 | |
|  			else
 | |
|  				(void) TIFFSeekFile(tif,
 | |
|  				    dircount16*20, SEEK_CUR);
 | |
| -			if (!ReadOK(tif, nextdir, sizeof (uint64_t))) {
 | |
| +			if (!ReadOK(tif, nextdiroff, sizeof (uint64_t))) {
 | |
|  				TIFFErrorExt(tif->tif_clientdata, module,
 | |
|                                               "%s: Error fetching directory link",
 | |
|  				    tif->tif_name);
 | |
|  				return (0);
 | |
|  			}
 | |
|  			if (tif->tif_flags & TIFF_SWAB)
 | |
| -				TIFFSwabLong8(nextdir);
 | |
| +				TIFFSwabLong8(nextdiroff);
 | |
|  		}
 | |
| -		return (1);
 | |
|  	}
 | |
| +	if (*nextdiroff != 0) {
 | |
| +		(*nextdirnum)++;
 | |
| +		/* Check next directory for IFD looping and if so, set it as last directory. */
 | |
| +		if (!_TIFFCheckDirNumberAndOffset(tif, *nextdirnum, *nextdiroff)) {
 | |
| +			TIFFWarningExt(tif->tif_clientdata, module, "the next directory %"PRIu16" at offset 0x%"PRIx64" (%"PRIu64") might be an IFD loop. Treating directory %"PRIu16" as last directory",
 | |
| +				*nextdirnum, *nextdiroff, *nextdiroff, *nextdirnum-1);
 | |
| +			*nextdiroff = 0;
 | |
| +			(*nextdirnum)--;
 | |
| +		}
 | |
| +	}
 | |
| +	return (1);
 | |
|  }
 | |
|  
 | |
|  /*
 | |
| @@ -1666,14 +1685,16 @@ uint16_t
 | |
|  TIFFNumberOfDirectories(TIFF* tif)
 | |
|  {
 | |
|  	static const char module[] = "TIFFNumberOfDirectories";
 | |
| -	uint64_t nextdir;
 | |
| +	uint64_t nextdiroff;
 | |
| +	uint16_t nextdirnum;
 | |
|  	uint16_t n;
 | |
|  	if (!(tif->tif_flags&TIFF_BIGTIFF))
 | |
| -		nextdir = tif->tif_header.classic.tiff_diroff;
 | |
| +		nextdiroff = tif->tif_header.classic.tiff_diroff;
 | |
|  	else
 | |
| -		nextdir = tif->tif_header.big.tiff_diroff;
 | |
| +		nextdiroff = tif->tif_header.big.tiff_diroff;
 | |
| +	nextdirnum = 0;
 | |
|  	n = 0;
 | |
| -	while (nextdir != 0 && TIFFAdvanceDirectory(tif, &nextdir, NULL))
 | |
| +	while (nextdiroff != 0 && TIFFAdvanceDirectory(tif, &nextdiroff, NULL, &nextdirnum))
 | |
|          {
 | |
|                  if (n != 65535) {
 | |
|                          ++n;
 | |
| @@ -1696,28 +1717,30 @@ TIFFNumberOfDirectories(TIFF* tif)
 | |
|  int
 | |
|  TIFFSetDirectory(TIFF* tif, uint16_t dirn)
 | |
|  {
 | |
| -	uint64_t nextdir;
 | |
| +	uint64_t nextdiroff;
 | |
| +	uint16_t nextdirnum;
 | |
|  	uint16_t n;
 | |
|  
 | |
|  	if (!(tif->tif_flags&TIFF_BIGTIFF))
 | |
| -		nextdir = tif->tif_header.classic.tiff_diroff;
 | |
| +		nextdiroff = tif->tif_header.classic.tiff_diroff;
 | |
|  	else
 | |
| -		nextdir = tif->tif_header.big.tiff_diroff;
 | |
| -	for (n = dirn; n > 0 && nextdir != 0; n--)
 | |
| -		if (!TIFFAdvanceDirectory(tif, &nextdir, NULL))
 | |
| +		nextdiroff = tif->tif_header.big.tiff_diroff;
 | |
| +	nextdirnum = 0;
 | |
| +	for (n = dirn; n > 0 && nextdiroff != 0; n--)
 | |
| +		if (!TIFFAdvanceDirectory(tif, &nextdiroff, NULL, &nextdirnum))
 | |
|  			return (0);
 | |
| -	tif->tif_nextdiroff = nextdir;
 | |
| +	/* If the n-th directory could not be reached (does not exist), 
 | |
| +	 * return here without touching anything further. */
 | |
| +	if (nextdiroff == 0 || n > 0)
 | |
| +		return (0);
 | |
| +
 | |
| +	tif->tif_nextdiroff = nextdiroff;
 | |
|  	/*
 | |
|  	 * Set curdir to the actual directory index.  The
 | |
|  	 * -1 is because TIFFReadDirectory will increment
 | |
|  	 * tif_curdir after successfully reading the directory.
 | |
|  	 */
 | |
|  	tif->tif_curdir = (dirn - n) - 1;
 | |
| -	/*
 | |
| -	 * Reset tif_dirnumber counter and start new list of seen directories.
 | |
| -	 * We need this to prevent IFD loops.
 | |
| -	 */
 | |
| -	tif->tif_dirnumber = 0;
 | |
|  	return (TIFFReadDirectory(tif));
 | |
|  }
 | |
|  
 | |
| @@ -1730,13 +1753,42 @@ TIFFSetDirectory(TIFF* tif, uint16_t dirn)
 | |
|  int
 | |
|  TIFFSetSubDirectory(TIFF* tif, uint64_t diroff)
 | |
|  {
 | |
| -	tif->tif_nextdiroff = diroff;
 | |
| -	/*
 | |
| -	 * Reset tif_dirnumber counter and start new list of seen directories.
 | |
| -	 * We need this to prevent IFD loops.
 | |
| +	/* Match nextdiroff and curdir for consistent IFD-loop checking. 
 | |
| +	 * Only with TIFFSetSubDirectory() the IFD list can be corrupted with invalid offsets
 | |
| +	 * within the main IFD tree.
 | |
| +	 * In the case of several subIFDs of a main image, 
 | |
| +	 * there are two possibilities that are not even mutually exclusive.
 | |
| +	 * a.) The subIFD tag contains an array with all offsets of the subIFDs.
 | |
| +	 * b.) The SubIFDs are concatenated with their NextIFD parameters.
 | |
| +	 * (refer to https://www.awaresystems.be/imaging/tiff/specification/TIFFPM6.pdf.)
 | |
|  	 */
 | |
| -	tif->tif_dirnumber = 0;
 | |
| -	return (TIFFReadDirectory(tif));
 | |
| +	int retval;
 | |
| +	uint16_t curdir = 0;
 | |
| +	int8_t probablySubIFD = 0;
 | |
| +	if (diroff == 0) {
 | |
| +		/* Special case to invalidate the tif_lastdiroff member. */
 | |
| +		tif->tif_curdir = 65535;
 | |
| +	} else {
 | |
| +		if (!_TIFFGetDirNumberFromOffset(tif, diroff, &curdir)) {
 | |
| +			/* Non-existing offsets might point to a SubIFD or invalid IFD.*/
 | |
| +			probablySubIFD = 1;
 | |
| +		}
 | |
| +		/* -1 because TIFFReadDirectory() will increment tif_curdir. */
 | |
| +		tif->tif_curdir = curdir - 1;
 | |
| +	}
 | |
| +
 | |
| +	tif->tif_nextdiroff = diroff;
 | |
| +	retval = TIFFReadDirectory(tif);
 | |
| +	/* If failed, curdir was not incremented in TIFFReadDirectory(), so set it back. */
 | |
| +	if (!retval )tif->tif_curdir++; 
 | |
| +	if (retval && probablySubIFD) {
 | |
| +		/* Reset IFD list to start new one for SubIFD chain and also start SubIFD chain with tif_curdir=0. */
 | |
| +		tif->tif_dirnumber = 0; 
 | |
| +		tif->tif_curdir = 0; /* first directory of new chain */
 | |
| +		/* add this offset to new IFD list */
 | |
| +		_TIFFCheckDirNumberAndOffset(tif, tif->tif_curdir, diroff);
 | |
| +	}
 | |
| +	return (retval);
 | |
|  }
 | |
|  
 | |
|  /*
 | |
| @@ -1760,12 +1812,15 @@ TIFFLastDirectory(TIFF* tif)
 | |
|  
 | |
|  /*
 | |
|   * Unlink the specified directory from the directory chain.
 | |
| + * Note: First directory starts with number dirn=1. 
 | |
| + * This is different to TIFFSetDirectory() where the first directory starts with zero.
 | |
|   */
 | |
|  int
 | |
|  TIFFUnlinkDirectory(TIFF* tif, uint16_t dirn)
 | |
|  {
 | |
|  	static const char module[] = "TIFFUnlinkDirectory";
 | |
|  	uint64_t nextdir;
 | |
| +	uint16_t nextdirnum;
 | |
|  	uint64_t off;
 | |
|  	uint16_t n;
 | |
|  
 | |
| @@ -1789,19 +1844,21 @@ TIFFUnlinkDirectory(TIFF* tif, uint16_t dirn)
 | |
|  		nextdir = tif->tif_header.big.tiff_diroff;
 | |
|  		off = 8;
 | |
|  	}
 | |
| +	nextdirnum = 0;		/* First directory is dirn=0 */
 | |
| +
 | |
|  	for (n = dirn-1; n > 0; n--) {
 | |
|  		if (nextdir == 0) {
 | |
|  			TIFFErrorExt(tif->tif_clientdata, module, "Directory %"PRIu16" does not exist", dirn);
 | |
|  			return (0);
 | |
|  		}
 | |
| -		if (!TIFFAdvanceDirectory(tif, &nextdir, &off))
 | |
| +		if (!TIFFAdvanceDirectory(tif, &nextdir, &off, &nextdirnum))
 | |
|  			return (0);
 | |
|  	}
 | |
|  	/*
 | |
|  	 * Advance to the directory to be unlinked and fetch
 | |
|  	 * the offset of the directory that follows.
 | |
|  	 */
 | |
| -	if (!TIFFAdvanceDirectory(tif, &nextdir, NULL))
 | |
| +	if (!TIFFAdvanceDirectory(tif, &nextdir, NULL, &nextdirnum))
 | |
|  		return (0);
 | |
|  	/*
 | |
|  	 * Go back and patch the link field of the preceding
 | |
| diff --git a/libtiff/tif_dir.h b/libtiff/tif_dir.h
 | |
| index 0c251c9e..deaa4594 100644
 | |
| --- a/libtiff/tif_dir.h
 | |
| +++ b/libtiff/tif_dir.h
 | |
| @@ -302,6 +302,8 @@ extern int _TIFFMergeFields(TIFF*, const TIFFField[], uint32_t);
 | |
|  extern const TIFFField* _TIFFFindOrRegisterField(TIFF *, uint32_t, TIFFDataType);
 | |
|  extern  TIFFField* _TIFFCreateAnonField(TIFF *, uint32_t, TIFFDataType);
 | |
|  extern int _TIFFCheckFieldIsValidForCodec(TIFF *tif, ttag_t tag);
 | |
| +extern int _TIFFCheckDirNumberAndOffset(TIFF *tif, uint16_t dirn, uint64_t diroff);
 | |
| +extern int _TIFFGetDirNumberFromOffset(TIFF *tif, uint64_t diroff, uint16_t *dirn);
 | |
|  
 | |
|  #if defined(__cplusplus)
 | |
|  }
 | |
| diff --git a/libtiff/tif_dirread.c b/libtiff/tif_dirread.c
 | |
| index 32653f04..f4c1b08d 100644
 | |
| --- a/libtiff/tif_dirread.c
 | |
| +++ b/libtiff/tif_dirread.c
 | |
| @@ -159,7 +159,6 @@ static void TIFFReadDirectoryFindFieldInfo(TIFF* tif, uint16_t tagid, uint32_t*
 | |
|  
 | |
|  static int EstimateStripByteCounts(TIFF* tif, TIFFDirEntry* dir, uint16_t dircount);
 | |
|  static void MissingRequired(TIFF*, const char*);
 | |
| -static int TIFFCheckDirOffset(TIFF* tif, uint64_t diroff);
 | |
|  static int CheckDirCount(TIFF*, TIFFDirEntry*, uint32_t);
 | |
|  static uint16_t TIFFFetchDirectory(TIFF* tif, uint64_t diroff, TIFFDirEntry** pdir, uint64_t* nextdiroff);
 | |
|  static int TIFFFetchNormalTag(TIFF*, TIFFDirEntry*, int recover);
 | |
| @@ -3922,12 +3921,19 @@ TIFFReadDirectory(TIFF* tif)
 | |
|      int bitspersample_read = FALSE;
 | |
|          int color_channels;
 | |
|  
 | |
| -	tif->tif_diroff=tif->tif_nextdiroff;
 | |
| -	if (!TIFFCheckDirOffset(tif,tif->tif_nextdiroff))
 | |
| -		return 0;           /* last offset or bad offset (IFD looping) */
 | |
| -	(*tif->tif_cleanup)(tif);   /* cleanup any previous compression state */
 | |
| -	tif->tif_curdir++;
 | |
| -        nextdiroff = tif->tif_nextdiroff;
 | |
| +	if (tif->tif_nextdiroff == 0) {
 | |
| +		/* In this special case, tif_diroff needs also to be set to 0. */
 | |
| +		tif->tif_diroff = tif->tif_nextdiroff;
 | |
| +		return 0;           /* last offset, thus no checking necessary */
 | |
| +	}
 | |
| +
 | |
| +	nextdiroff = tif->tif_nextdiroff;
 | |
| +	/* tif_curdir++ and tif_nextdiroff should only be updated after SUCCESSFUL reading of the directory. Otherwise, invalid IFD offsets could corrupt the IFD list. */
 | |
| +	if (!_TIFFCheckDirNumberAndOffset(tif, tif->tif_curdir + 1, nextdiroff)) {
 | |
| +		TIFFWarningExt(tif->tif_clientdata, module,
 | |
| +			"Didn't read next directory due to IFD looping at offset 0x%"PRIx64" (%"PRIu64") to offset 0x%"PRIx64" (%"PRIu64")", tif->tif_diroff, tif->tif_diroff, nextdiroff, nextdiroff);
 | |
| +		return 0;           /* bad offset (IFD looping) */
 | |
| +	}
 | |
|  	dircount=TIFFFetchDirectory(tif,nextdiroff,&dir,&tif->tif_nextdiroff);
 | |
|  	if (!dircount)
 | |
|  	{
 | |
| @@ -3935,6 +3941,11 @@ TIFFReadDirectory(TIFF* tif)
 | |
|  		    "Failed to read directory at offset %" PRIu64, nextdiroff);
 | |
|  		return 0;
 | |
|  	}
 | |
| +	/* Set global values after a valid directory has been fetched.
 | |
| +	 * tif_diroff is already set to nextdiroff in TIFFFetchDirectory() in the beginning. */
 | |
| +	tif->tif_curdir++;
 | |
| +	(*tif->tif_cleanup)(tif);   /* cleanup any previous compression state */
 | |
| +
 | |
|  	TIFFReadDirectoryCheckOrder(tif,dir,dircount);
 | |
|  
 | |
|          /*
 | |
| @@ -5026,53 +5037,127 @@ MissingRequired(TIFF* tif, const char* tagname)
 | |
|  }
 | |
|  
 | |
|  /*
 | |
| - * Check the directory offset against the list of already seen directory
 | |
| - * offsets. This is a trick to prevent IFD looping. The one can create TIFF
 | |
| - * file with looped directory pointers. We will maintain a list of already
 | |
| - * seen directories and check every IFD offset against that list.
 | |
| + * Check the directory number and offset against the list of already seen
 | |
| + * directory numbers and offsets. This is a trick to prevent IFD looping.
 | |
| + * The one can create TIFF file with looped directory pointers. We will
 | |
| + * maintain a list of already seen directories and check every IFD offset
 | |
| + * and its IFD number against that list. However, the offset of an IFD number
 | |
| + * can change - e.g. when writing updates to file.
 | |
| + * Returns 1 if all is ok; 0 if last directory or IFD loop is encountered,
 | |
| + * or an error has occured.
 | |
|   */
 | |
| -static int
 | |
| -TIFFCheckDirOffset(TIFF* tif, uint64_t diroff)
 | |
| +int
 | |
| +_TIFFCheckDirNumberAndOffset(TIFF *tif, uint16_t dirn, uint64_t diroff)
 | |
|  {
 | |
|  	uint16_t n;
 | |
|  
 | |
|  	if (diroff == 0)			/* no more directories */
 | |
|  		return 0;
 | |
|  	if (tif->tif_dirnumber == 65535) {
 | |
| -	    TIFFErrorExt(tif->tif_clientdata, "TIFFCheckDirOffset",
 | |
| -			 "Cannot handle more than 65535 TIFF directories");
 | |
| -	    return 0;
 | |
| +		TIFFErrorExt(tif->tif_clientdata, "_TIFFCheckDirNumberAndOffset",
 | |
| +			"Cannot handle more than 65535 TIFF directories");
 | |
| +		return 0;
 | |
|  	}
 | |
|  
 | |
| -	for (n = 0; n < tif->tif_dirnumber && tif->tif_dirlist; n++) {
 | |
| -		if (tif->tif_dirlist[n] == diroff)
 | |
| -			return 0;
 | |
| +	/* Check if offset is already in the list:
 | |
| +	 * - yes: check, if offset is at the same IFD number - if not, it is an IFD loop
 | |
| +	 * -  no: add to list or update offset at that IFD number
 | |
| +	 */
 | |
| +	for (n = 0; n < tif->tif_dirnumber && tif->tif_dirlistdirn && tif->tif_dirlistoff; n++) {
 | |
| +		if (tif->tif_dirlistoff[n] == diroff) {
 | |
| +			if (tif->tif_dirlistdirn[n] == dirn) {
 | |
| +				return 1;
 | |
| +			} else {
 | |
| +				TIFFWarningExt(tif->tif_clientdata, "_TIFFCheckDirNumberAndOffset",
 | |
| +					"TIFF directory %"PRIu16" has IFD looping to directory %"PRIu16" at offset 0x%"PRIx64" (%"PRIu64")",
 | |
| +					dirn-1, tif->tif_dirlistdirn[n], diroff, diroff);
 | |
| +				return 0;
 | |
| +			}
 | |
| +		}
 | |
| +	}
 | |
| +	/* Check if offset of an IFD has been changed and update offset of that IFD number. */
 | |
| +	if (dirn < tif->tif_dirnumber && tif->tif_dirlistdirn && tif->tif_dirlistoff) {
 | |
| +		/* tif_dirlistdirn can have IFD numbers dirn in random order */
 | |
| +		for (n = 0; n < tif->tif_dirnumber; n++) {
 | |
| +			if (tif->tif_dirlistdirn[n] == dirn) {
 | |
| +				tif->tif_dirlistoff[n] = diroff;
 | |
| +				return 1;
 | |
| +			}
 | |
| +		}
 | |
|  	}
 | |
|  
 | |
| +	/* Add IFD offset and dirn to IFD directory list */
 | |
|  	tif->tif_dirnumber++;
 | |
|  
 | |
| -	if (tif->tif_dirlist == NULL || tif->tif_dirnumber > tif->tif_dirlistsize) {
 | |
| -		uint64_t* new_dirlist;
 | |
| -
 | |
| +	if (tif->tif_dirlistoff == NULL || tif->tif_dirlistdirn == NULL || tif->tif_dirnumber > tif->tif_dirlistsize) {
 | |
| +		uint64_t *new_dirlist;
 | |
|  		/*
 | |
|  		 * XXX: Reduce memory allocation granularity of the dirlist
 | |
|  		 * array.
 | |
|  		 */
 | |
| -		new_dirlist = (uint64_t*)_TIFFCheckRealloc(tif, tif->tif_dirlist,
 | |
| -                                                   tif->tif_dirnumber, 2 * sizeof(uint64_t), "for IFD list");
 | |
| +		if (tif->tif_dirnumber >= 32768)
 | |
| +			tif->tif_dirlistsize = 65535;
 | |
| +		else
 | |
| +			tif->tif_dirlistsize = 2 * tif->tif_dirnumber;
 | |
| +
 | |
| +		new_dirlist = (uint64_t *)_TIFFCheckRealloc(tif, tif->tif_dirlistoff,
 | |
| +			tif->tif_dirlistsize, sizeof(uint64_t), "for IFD offset list");
 | |
|  		if (!new_dirlist)
 | |
|  			return 0;
 | |
| -		if( tif->tif_dirnumber >= 32768 )
 | |
| -		    tif->tif_dirlistsize = 65535;
 | |
| -		else
 | |
| -		    tif->tif_dirlistsize = 2 * tif->tif_dirnumber;
 | |
| -		tif->tif_dirlist = new_dirlist;
 | |
| +		tif->tif_dirlistoff = new_dirlist;
 | |
| +		new_dirlist = (uint64_t *)_TIFFCheckRealloc(tif, tif->tif_dirlistdirn,
 | |
| +			tif->tif_dirlistsize, sizeof(uint16_t), "for IFD dirnumber list");
 | |
| +		if (!new_dirlist)
 | |
| +			return 0;
 | |
| +		tif->tif_dirlistdirn = (uint16_t *)new_dirlist;
 | |
|  	}
 | |
|  
 | |
| -	tif->tif_dirlist[tif->tif_dirnumber - 1] = diroff;
 | |
| +	tif->tif_dirlistoff[tif->tif_dirnumber - 1] = diroff;
 | |
| +	tif->tif_dirlistdirn[tif->tif_dirnumber - 1] = dirn;
 | |
|  
 | |
|  	return 1;
 | |
| -}
 | |
| +}	/* --- _TIFFCheckDirNumberAndOffset() ---*/
 | |
| +
 | |
| +/*
 | |
| + * Retrieve the matching IFD directory number of a given IFD offset
 | |
| + * from the list of directories already seen.
 | |
| + * Returns 1 if the offset was in the list and the directory number
 | |
| + * can be returned.
 | |
| + * Otherwise returns 0 or if an error occured.
 | |
| + */
 | |
| +int
 | |
| +_TIFFGetDirNumberFromOffset(TIFF *tif, uint64_t diroff, uint16_t* dirn)
 | |
| +{
 | |
| +	uint16_t n;
 | |
| +
 | |
| +	if (diroff == 0)			/* no more directories */
 | |
| +		return 0;
 | |
| +	if (tif->tif_dirnumber == 65535) {
 | |
| +		TIFFErrorExt(tif->tif_clientdata, "_TIFFGetDirNumberFromOffset",
 | |
| +			"Cannot handle more than 65535 TIFF directories");
 | |
| +		return 0;
 | |
| +	}
 | |
| +
 | |
| +	/* Check if offset is already in the list and return matching directory number.
 | |
| +	 * Otherwise update IFD list using TIFFNumberOfDirectories() 
 | |
| +	 * and search again in IFD list.
 | |
| +	 */
 | |
| +	for (n = 0; n < tif->tif_dirnumber && tif->tif_dirlistoff && tif->tif_dirlistdirn; n++) {
 | |
| +		if (tif->tif_dirlistoff[n] == diroff) {
 | |
| +			*dirn = tif->tif_dirlistdirn[n];
 | |
| +			return 1;
 | |
| +		}
 | |
| +	}
 | |
| +	TIFFNumberOfDirectories(tif);
 | |
| +	for (n = 0; n < tif->tif_dirnumber && tif->tif_dirlistoff && tif->tif_dirlistdirn; n++) {
 | |
| +		if (tif->tif_dirlistoff[n] == diroff) {
 | |
| +			*dirn = tif->tif_dirlistdirn[n];
 | |
| +			return 1;
 | |
| +		}
 | |
| +	}
 | |
| +	return 0;
 | |
| +} /*--- _TIFFGetDirNumberFromOffset() ---*/
 | |
| +
 | |
|  
 | |
|  /*
 | |
|   * Check the count field of a directory entry against a known value.  The
 | |
| diff --git a/libtiff/tif_open.c b/libtiff/tif_open.c
 | |
| index 549f56ce..85c2af47 100644
 | |
| --- a/libtiff/tif_open.c
 | |
| +++ b/libtiff/tif_open.c
 | |
| @@ -354,7 +354,8 @@ TIFFClientOpen(
 | |
|  			goto bad;
 | |
|  		tif->tif_diroff = 0;
 | |
|  		tif->tif_lastdiroff = 0;
 | |
| -		tif->tif_dirlist = NULL;
 | |
| +		tif->tif_dirlistoff = NULL;
 | |
| +		tif->tif_dirlistdirn = NULL;
 | |
|  		tif->tif_dirlistsize = 0;
 | |
|  		tif->tif_dirnumber = 0;
 | |
|  		return (tif);
 | |
| diff --git a/libtiff/tiffiop.h b/libtiff/tiffiop.h
 | |
| index 4e8bdac2..8e0e2454 100644
 | |
| --- a/libtiff/tiffiop.h
 | |
| +++ b/libtiff/tiffiop.h
 | |
| @@ -118,7 +118,8 @@ struct tiff {
 | |
|  	uint64_t               tif_diroff;       /* file offset of current directory */
 | |
|  	uint64_t               tif_nextdiroff;   /* file offset of following directory */
 | |
|  	uint64_t               tif_lastdiroff;   /* file offset of last directory written so far */
 | |
| -	uint64_t*              tif_dirlist;      /* list of offsets to already seen directories to prevent IFD looping */
 | |
| +	uint64_t*              tif_dirlistoff;   /* list of offsets to already seen directories to prevent IFD looping */
 | |
| +	uint16_t*              tif_dirlistdirn;  /* list of directory numbers to already seen directories to prevent IFD looping */
 | |
|  	uint16_t               tif_dirlistsize;  /* number of entries in offset list */
 | |
|  	uint16_t               tif_dirnumber;    /* number of already seen directories */
 | |
|  	TIFFDirectory        tif_dir;          /* internal rep of current directory */
 |