diff --git a/SOURCES/0019-CVE-2023-40090-Improved-IFD-Loop-Handling-fixes-455.patch b/SOURCES/0019-CVE-2023-40090-Improved-IFD-Loop-Handling-fixes-455.patch new file mode 100644 index 0000000..3c26802 --- /dev/null +++ b/SOURCES/0019-CVE-2023-40090-Improved-IFD-Loop-Handling-fixes-455.patch @@ -0,0 +1,548 @@ +From 98648d3b62d4f123915410b8cae47954f9223000 Mon Sep 17 00:00:00 2001 +From: Even Rouault +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 */ diff --git a/SOURCES/0020-CVE-2023-3618-tiffcrop-fix-553-by-considering-error-.patch b/SOURCES/0020-CVE-2023-3618-tiffcrop-fix-553-by-considering-error-.patch new file mode 100644 index 0000000..25a9201 --- /dev/null +++ b/SOURCES/0020-CVE-2023-3618-tiffcrop-fix-553-by-considering-error-.patch @@ -0,0 +1,36 @@ +From 979173fc37955d27c9550beb6c7ed1f2466c6ff4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Matej=20Mu=C5=BEila?= +Date: Wed, 4 Oct 2023 13:13:08 +0200 +Subject: [PATCH] (CVE-2023-3618) tiffcrop: fix 553 by considering error return + of writeSelections() + +Closes #553 + +See merge request libtiff/libtiff!485 + +(cherry picked from commit 7ead6c42c70636c0ffec2e9ff3f16d614e37fb7b) +--- + tools/tiffcrop.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/tools/tiffcrop.c b/tools/tiffcrop.c +index be72ec52..edb33e25 100644 +--- a/tools/tiffcrop.c ++++ b/tools/tiffcrop.c +@@ -2464,8 +2464,14 @@ main(int argc, char* argv[]) + { /* Whole image or sections not based on output page size */ + if (crop.selections > 0) + { +- writeSelections(in, &out, &crop, &image, &dump, seg_buffs, +- mp, argv[argc - 1], &next_page, total_pages); ++ if (writeSelections(in, &out, &crop, &image, &dump, ++ seg_buffs, mp, argv[argc - 1], ++ &next_page, total_pages)) ++ { ++ TIFFError("main", ++ "Unable to write new image selections"); ++ exit(EXIT_FAILURE); ++ } + } + else /* One file all images and sections */ + { diff --git a/SOURCES/0021-CVE-2023-40745-CVE-2023-41175-raw2tiff-fix-integer-o.patch b/SOURCES/0021-CVE-2023-40745-CVE-2023-41175-raw2tiff-fix-integer-o.patch new file mode 100644 index 0000000..b575769 --- /dev/null +++ b/SOURCES/0021-CVE-2023-40745-CVE-2023-41175-raw2tiff-fix-integer-o.patch @@ -0,0 +1,67 @@ +From fffa3dc40d30155a17487ba3edacb320cb29b873 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Matej=20Mu=C5=BEila?= +Date: Wed, 4 Oct 2023 13:20:28 +0200 +Subject: [PATCH] (CVE-2023-40745 CVE-2023-41175) raw2tiff: fix integer + overflow and bypass of the check (fixes #592) + +See merge request libtiff/libtiff!516 + +(cherry picked from commit 6ac40fefa1763ad08c6e9654d10910e462426f82) +--- + tools/raw2tiff.c | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/tools/raw2tiff.c b/tools/raw2tiff.c +index 0c6001f7..747ede7f 100644 +--- a/tools/raw2tiff.c ++++ b/tools/raw2tiff.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + + #ifdef HAVE_UNISTD_H + # include +@@ -101,6 +102,7 @@ main(int argc, char* argv[]) + int fd; + char *outfilename = NULL; + TIFF *out; ++ uint32_t temp_limit_check = 0; /* temp for integer overflow checking*/ + + uint32_t row, col, band; + int c; +@@ -217,6 +219,33 @@ main(int argc, char* argv[]) + if (guessSize(fd, dtype, hdr_size, nbands, swab, &width, &length) < 0) + return EXIT_FAILURE; + ++ /* check for integer overflow in */ ++ /* hdr_size + (*width) * (*length) * nbands * depth */ ++ ++ if ((width == 0) || (length == 0) ){ ++ fprintf(stderr, "Too large nbands value specified.\n"); ++ return (EXIT_FAILURE); ++ } ++ ++ temp_limit_check = nbands * depth; ++ ++ if ( !temp_limit_check || length > ( UINT_MAX / temp_limit_check ) ) { ++ fprintf(stderr, "Too large length size specified.\n"); ++ return (EXIT_FAILURE); ++ } ++ temp_limit_check = temp_limit_check * length; ++ ++ if ( !temp_limit_check || width > ( UINT_MAX / temp_limit_check ) ) { ++ fprintf(stderr, "Too large width size specified.\n"); ++ return (EXIT_FAILURE); ++ } ++ temp_limit_check = temp_limit_check * width; ++ ++ if ( !temp_limit_check || hdr_size > ( UINT_MAX - temp_limit_check ) ) { ++ fprintf(stderr, "Too large header size specified.\n"); ++ return (EXIT_FAILURE); ++ } ++ + if (outfilename == NULL) + outfilename = argv[optind+1]; + out = TIFFOpen(outfilename, "w"); diff --git a/SOURCES/0022-CVE-2023-6228-Merge-branch-fix_606_tiffcp_check_also.patch b/SOURCES/0022-CVE-2023-6228-Merge-branch-fix_606_tiffcp_check_also.patch new file mode 100644 index 0000000..c0c1e93 --- /dev/null +++ b/SOURCES/0022-CVE-2023-6228-Merge-branch-fix_606_tiffcp_check_also.patch @@ -0,0 +1,30 @@ +From b57347d203cf577a3abb02cb62aee84b82903fcf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Matej=20Mu=C5=BEila?= +Date: Thu, 23 Nov 2023 11:05:35 +0100 +Subject: [PATCH] (CVE-2023-6228) Merge branch + 'fix_606_tiffcp_check_also_input_compression_codec' into 'master' + +tiffcp: Fixes #606. Check also codec of input image, not only from output image. + +Closes #606 + +See merge request libtiff/libtiff!533 + +(cherry picked from commit 668d2c1a52fa48658bbf69615924b42b5a059f9e) +--- + tools/tiffcp.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/tools/tiffcp.c b/tools/tiffcp.c +index 07ed0ebc..9973dd4e 100644 +--- a/tools/tiffcp.c ++++ b/tools/tiffcp.c +@@ -732,6 +732,8 @@ tiffcp(TIFF* in, TIFF* out) + if( !TIFFIsCODECConfigured(compression) ) + return FALSE; + TIFFGetFieldDefaulted(in, TIFFTAG_COMPRESSION, &input_compression); ++ if (!TIFFIsCODECConfigured(input_compression)) ++ return FALSE; + TIFFGetFieldDefaulted(in, TIFFTAG_PHOTOMETRIC, &input_photometric); + if (input_compression == COMPRESSION_JPEG) { + /* Force conversion to RGB */ diff --git a/SPECS/libtiff.spec b/SPECS/libtiff.spec index 65bdcf8..92c8d64 100644 --- a/SPECS/libtiff.spec +++ b/SPECS/libtiff.spec @@ -1,7 +1,7 @@ Summary: Library of functions for manipulating TIFF format image files Name: libtiff Version: 4.4.0 -Release: 10%{?dist} +Release: 12%{?dist} License: libtiff URL: http://www.simplesystems.org/libtiff/ @@ -31,8 +31,10 @@ Patch0015: 0015-CVE-2023-26965-tiffcrop-Do-not-reuse-input-buffer-fo.patch Patch0016: 0016-CVE-2023-3316-TIFFClose-avoid-NULL-pointer-dereferen.patch Patch0017: 0017-CVE-2023-26966-tif_luv-Check-and-correct-for-NaN-dat.patch Patch0018: 0018-CVE-2023-3576-Fix-memory-leak-in-tiffcrop.c.patch - - +Patch0019: 0019-CVE-2023-40090-Improved-IFD-Loop-Handling-fixes-455.patch +Patch0020: 0020-CVE-2023-3618-tiffcrop-fix-553-by-considering-error-.patch +Patch0021: 0021-CVE-2023-40745-CVE-2023-41175-raw2tiff-fix-integer-o.patch +Patch0022: 0022-CVE-2023-6228-Merge-branch-fix_606_tiffcp_check_also.patch BuildRequires: gcc, gcc-c++ BuildRequires: zlib-devel libjpeg-devel jbigkit-devel libzstd-devel libwebp-devel @@ -185,6 +187,14 @@ find html -name 'Makefile*' | xargs rm %{_mandir}/man1/* %changelog +* Thu Nov 23 2023 Matej Mužila - 4.4.0-12 +- Fix CVE-2023-6228 +- Resolves: RHEL-10084 + +* Wed Oct 04 2023 Matej Mužila - 4.4.0-11 +- Fix CVE-2023-40090 CVE-2023-3618 CVE-2023-40745 CVE-2023-41175 +- Resolves: RHEL-5458 RHEL-5455 RHEL-5405 RHEL-5450 + * Tue Aug 08 2023 Matej Mužila - 4.4.0-10 - Fix CVE-2023-26965 CVE-2023-3316 CVE-2023-26966 CVE-2023-3576 - Resolves: CVE-2023-26965 CVE-2023-3316 CVE-2023-26966 CVE-2023-3576