From 281fa3cf0e0e8a44b93478c63d90dbfb64359e88 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 5 Dec 2021 14:37:46 +0100 Subject: [PATCH] TIFFReadDirectory: fix OJPEG hack (fixes #319) to avoid having the size of the strip arrays inconsistent with the number of strips returned by TIFFNumberOfStrips(), which may cause out-ouf-bounds array read afterwards. One of the OJPEG hack that alters SamplesPerPixel may influence the number of strips. Hence compute tif_dir.td_nstrips only afterwards. CVE: CVE-2022-1354 Upstream-Status: Backport [https://gitlab.com/libtiff/libtiff/-/commit/87f580f39011109b3bb5f6eca13fac543a542798] Signed-off-by: Yi Zhao --- libtiff/tif_dirread.c | 162 ++++++++++++++++++++++-------------------- 1 file changed, 83 insertions(+), 79 deletions(-) diff --git a/libtiff/tif_dirread.c b/libtiff/tif_dirread.c index a31109a..d7cccbe 100644 --- a/libtiff/tif_dirread.c +++ b/libtiff/tif_dirread.c @@ -3794,50 +3794,7 @@ TIFFReadDirectory(TIFF* tif) MissingRequired(tif,"ImageLength"); goto bad; } - /* - * Setup appropriate structures (by strip or by tile) - */ - if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) { - tif->tif_dir.td_nstrips = TIFFNumberOfStrips(tif); - tif->tif_dir.td_tilewidth = tif->tif_dir.td_imagewidth; - tif->tif_dir.td_tilelength = tif->tif_dir.td_rowsperstrip; - tif->tif_dir.td_tiledepth = tif->tif_dir.td_imagedepth; - tif->tif_flags &= ~TIFF_ISTILED; - } else { - tif->tif_dir.td_nstrips = TIFFNumberOfTiles(tif); - tif->tif_flags |= TIFF_ISTILED; - } - if (!tif->tif_dir.td_nstrips) { - TIFFErrorExt(tif->tif_clientdata, module, - "Cannot handle zero number of %s", - isTiled(tif) ? "tiles" : "strips"); - goto bad; - } - tif->tif_dir.td_stripsperimage = tif->tif_dir.td_nstrips; - if (tif->tif_dir.td_planarconfig == PLANARCONFIG_SEPARATE) - tif->tif_dir.td_stripsperimage /= tif->tif_dir.td_samplesperpixel; - if (!TIFFFieldSet(tif, FIELD_STRIPOFFSETS)) { -#ifdef OJPEG_SUPPORT - if ((tif->tif_dir.td_compression==COMPRESSION_OJPEG) && - (isTiled(tif)==0) && - (tif->tif_dir.td_nstrips==1)) { - /* - * XXX: OJPEG hack. - * If a) compression is OJPEG, b) it's not a tiled TIFF, - * and c) the number of strips is 1, - * then we tolerate the absence of stripoffsets tag, - * because, presumably, all required data is in the - * JpegInterchangeFormat stream. - */ - TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS); - } else -#endif - { - MissingRequired(tif, - isTiled(tif) ? "TileOffsets" : "StripOffsets"); - goto bad; - } - } + /* * Second pass: extract other information. */ @@ -4042,41 +3999,6 @@ TIFFReadDirectory(TIFF* tif) } /* -- if (!dp->tdir_ignore) */ } /* -- for-loop -- */ - if( tif->tif_mode == O_RDWR && - tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 && - tif->tif_dir.td_stripoffset_entry.tdir_count == 0 && - tif->tif_dir.td_stripoffset_entry.tdir_type == 0 && - tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 && - tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 && - tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 && - tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 && - tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0 ) - { - /* Directory typically created with TIFFDeferStrileArrayWriting() */ - TIFFSetupStrips(tif); - } - else if( !(tif->tif_flags&TIFF_DEFERSTRILELOAD) ) - { - if( tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 ) - { - if (!TIFFFetchStripThing(tif,&(tif->tif_dir.td_stripoffset_entry), - tif->tif_dir.td_nstrips, - &tif->tif_dir.td_stripoffset_p)) - { - goto bad; - } - } - if( tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 ) - { - if (!TIFFFetchStripThing(tif,&(tif->tif_dir.td_stripbytecount_entry), - tif->tif_dir.td_nstrips, - &tif->tif_dir.td_stripbytecount_p)) - { - goto bad; - } - } - } - /* * OJPEG hack: * - If a) compression is OJPEG, and b) photometric tag is missing, @@ -4147,6 +4069,88 @@ TIFFReadDirectory(TIFF* tif) } } + /* + * Setup appropriate structures (by strip or by tile) + * We do that only after the above OJPEG hack which alters SamplesPerPixel + * and thus influences the number of strips in the separate planarconfig. + */ + if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) { + tif->tif_dir.td_nstrips = TIFFNumberOfStrips(tif); + tif->tif_dir.td_tilewidth = tif->tif_dir.td_imagewidth; + tif->tif_dir.td_tilelength = tif->tif_dir.td_rowsperstrip; + tif->tif_dir.td_tiledepth = tif->tif_dir.td_imagedepth; + tif->tif_flags &= ~TIFF_ISTILED; + } else { + tif->tif_dir.td_nstrips = TIFFNumberOfTiles(tif); + tif->tif_flags |= TIFF_ISTILED; + } + if (!tif->tif_dir.td_nstrips) { + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot handle zero number of %s", + isTiled(tif) ? "tiles" : "strips"); + goto bad; + } + tif->tif_dir.td_stripsperimage = tif->tif_dir.td_nstrips; + if (tif->tif_dir.td_planarconfig == PLANARCONFIG_SEPARATE) + tif->tif_dir.td_stripsperimage /= tif->tif_dir.td_samplesperpixel; + if (!TIFFFieldSet(tif, FIELD_STRIPOFFSETS)) { +#ifdef OJPEG_SUPPORT + if ((tif->tif_dir.td_compression==COMPRESSION_OJPEG) && + (isTiled(tif)==0) && + (tif->tif_dir.td_nstrips==1)) { + /* + * XXX: OJPEG hack. + * If a) compression is OJPEG, b) it's not a tiled TIFF, + * and c) the number of strips is 1, + * then we tolerate the absence of stripoffsets tag, + * because, presumably, all required data is in the + * JpegInterchangeFormat stream. + */ + TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS); + } else +#endif + { + MissingRequired(tif, + isTiled(tif) ? "TileOffsets" : "StripOffsets"); + goto bad; + } + } + + if( tif->tif_mode == O_RDWR && + tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 && + tif->tif_dir.td_stripoffset_entry.tdir_count == 0 && + tif->tif_dir.td_stripoffset_entry.tdir_type == 0 && + tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 && + tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 && + tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 && + tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 && + tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0 ) + { + /* Directory typically created with TIFFDeferStrileArrayWriting() */ + TIFFSetupStrips(tif); + } + else if( !(tif->tif_flags&TIFF_DEFERSTRILELOAD) ) + { + if( tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 ) + { + if (!TIFFFetchStripThing(tif,&(tif->tif_dir.td_stripoffset_entry), + tif->tif_dir.td_nstrips, + &tif->tif_dir.td_stripoffset_p)) + { + goto bad; + } + } + if( tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 ) + { + if (!TIFFFetchStripThing(tif,&(tif->tif_dir.td_stripbytecount_entry), + tif->tif_dir.td_nstrips, + &tif->tif_dir.td_stripbytecount_p)) + { + goto bad; + } + } + } + /* * Make sure all non-color channels are extrasamples. * If it's not the case, define them as such.