1From 281fa3cf0e0e8a44b93478c63d90dbfb64359e88 Mon Sep 17 00:00:00 2001 2From: Even Rouault <even.rouault@spatialys.com> 3Date: Sun, 5 Dec 2021 14:37:46 +0100 4Subject: [PATCH] TIFFReadDirectory: fix OJPEG hack (fixes #319) 5 6to avoid having the size of the strip arrays inconsistent with the 7number of strips returned by TIFFNumberOfStrips(), which may cause 8out-ouf-bounds array read afterwards. 9 10One of the OJPEG hack that alters SamplesPerPixel may influence the 11number of strips. Hence compute tif_dir.td_nstrips only afterwards. 12 13CVE: CVE-2022-1354 14 15Upstream-Status: Backport 16[https://gitlab.com/libtiff/libtiff/-/commit/87f580f39011109b3bb5f6eca13fac543a542798] 17 18Signed-off-by: Yi Zhao <yi.zhao@windriver.com> 19 20--- 21 libtiff/tif_dirread.c | 162 ++++++++++++++++++++++-------------------- 22 1 file changed, 83 insertions(+), 79 deletions(-) 23 24diff --git a/libtiff/tif_dirread.c b/libtiff/tif_dirread.c 25index a31109a..d7cccbe 100644 26--- a/libtiff/tif_dirread.c 27+++ b/libtiff/tif_dirread.c 28@@ -3794,50 +3794,7 @@ TIFFReadDirectory(TIFF* tif) 29 MissingRequired(tif,"ImageLength"); 30 goto bad; 31 } 32- /* 33- * Setup appropriate structures (by strip or by tile) 34- */ 35- if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) { 36- tif->tif_dir.td_nstrips = TIFFNumberOfStrips(tif); 37- tif->tif_dir.td_tilewidth = tif->tif_dir.td_imagewidth; 38- tif->tif_dir.td_tilelength = tif->tif_dir.td_rowsperstrip; 39- tif->tif_dir.td_tiledepth = tif->tif_dir.td_imagedepth; 40- tif->tif_flags &= ~TIFF_ISTILED; 41- } else { 42- tif->tif_dir.td_nstrips = TIFFNumberOfTiles(tif); 43- tif->tif_flags |= TIFF_ISTILED; 44- } 45- if (!tif->tif_dir.td_nstrips) { 46- TIFFErrorExt(tif->tif_clientdata, module, 47- "Cannot handle zero number of %s", 48- isTiled(tif) ? "tiles" : "strips"); 49- goto bad; 50- } 51- tif->tif_dir.td_stripsperimage = tif->tif_dir.td_nstrips; 52- if (tif->tif_dir.td_planarconfig == PLANARCONFIG_SEPARATE) 53- tif->tif_dir.td_stripsperimage /= tif->tif_dir.td_samplesperpixel; 54- if (!TIFFFieldSet(tif, FIELD_STRIPOFFSETS)) { 55-#ifdef OJPEG_SUPPORT 56- if ((tif->tif_dir.td_compression==COMPRESSION_OJPEG) && 57- (isTiled(tif)==0) && 58- (tif->tif_dir.td_nstrips==1)) { 59- /* 60- * XXX: OJPEG hack. 61- * If a) compression is OJPEG, b) it's not a tiled TIFF, 62- * and c) the number of strips is 1, 63- * then we tolerate the absence of stripoffsets tag, 64- * because, presumably, all required data is in the 65- * JpegInterchangeFormat stream. 66- */ 67- TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS); 68- } else 69-#endif 70- { 71- MissingRequired(tif, 72- isTiled(tif) ? "TileOffsets" : "StripOffsets"); 73- goto bad; 74- } 75- } 76+ 77 /* 78 * Second pass: extract other information. 79 */ 80@@ -4042,41 +3999,6 @@ TIFFReadDirectory(TIFF* tif) 81 } /* -- if (!dp->tdir_ignore) */ 82 } /* -- for-loop -- */ 83 84- if( tif->tif_mode == O_RDWR && 85- tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 && 86- tif->tif_dir.td_stripoffset_entry.tdir_count == 0 && 87- tif->tif_dir.td_stripoffset_entry.tdir_type == 0 && 88- tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 && 89- tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 && 90- tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 && 91- tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 && 92- tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0 ) 93- { 94- /* Directory typically created with TIFFDeferStrileArrayWriting() */ 95- TIFFSetupStrips(tif); 96- } 97- else if( !(tif->tif_flags&TIFF_DEFERSTRILELOAD) ) 98- { 99- if( tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 ) 100- { 101- if (!TIFFFetchStripThing(tif,&(tif->tif_dir.td_stripoffset_entry), 102- tif->tif_dir.td_nstrips, 103- &tif->tif_dir.td_stripoffset_p)) 104- { 105- goto bad; 106- } 107- } 108- if( tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 ) 109- { 110- if (!TIFFFetchStripThing(tif,&(tif->tif_dir.td_stripbytecount_entry), 111- tif->tif_dir.td_nstrips, 112- &tif->tif_dir.td_stripbytecount_p)) 113- { 114- goto bad; 115- } 116- } 117- } 118- 119 /* 120 * OJPEG hack: 121 * - If a) compression is OJPEG, and b) photometric tag is missing, 122@@ -4147,6 +4069,88 @@ TIFFReadDirectory(TIFF* tif) 123 } 124 } 125 126+ /* 127+ * Setup appropriate structures (by strip or by tile) 128+ * We do that only after the above OJPEG hack which alters SamplesPerPixel 129+ * and thus influences the number of strips in the separate planarconfig. 130+ */ 131+ if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) { 132+ tif->tif_dir.td_nstrips = TIFFNumberOfStrips(tif); 133+ tif->tif_dir.td_tilewidth = tif->tif_dir.td_imagewidth; 134+ tif->tif_dir.td_tilelength = tif->tif_dir.td_rowsperstrip; 135+ tif->tif_dir.td_tiledepth = tif->tif_dir.td_imagedepth; 136+ tif->tif_flags &= ~TIFF_ISTILED; 137+ } else { 138+ tif->tif_dir.td_nstrips = TIFFNumberOfTiles(tif); 139+ tif->tif_flags |= TIFF_ISTILED; 140+ } 141+ if (!tif->tif_dir.td_nstrips) { 142+ TIFFErrorExt(tif->tif_clientdata, module, 143+ "Cannot handle zero number of %s", 144+ isTiled(tif) ? "tiles" : "strips"); 145+ goto bad; 146+ } 147+ tif->tif_dir.td_stripsperimage = tif->tif_dir.td_nstrips; 148+ if (tif->tif_dir.td_planarconfig == PLANARCONFIG_SEPARATE) 149+ tif->tif_dir.td_stripsperimage /= tif->tif_dir.td_samplesperpixel; 150+ if (!TIFFFieldSet(tif, FIELD_STRIPOFFSETS)) { 151+#ifdef OJPEG_SUPPORT 152+ if ((tif->tif_dir.td_compression==COMPRESSION_OJPEG) && 153+ (isTiled(tif)==0) && 154+ (tif->tif_dir.td_nstrips==1)) { 155+ /* 156+ * XXX: OJPEG hack. 157+ * If a) compression is OJPEG, b) it's not a tiled TIFF, 158+ * and c) the number of strips is 1, 159+ * then we tolerate the absence of stripoffsets tag, 160+ * because, presumably, all required data is in the 161+ * JpegInterchangeFormat stream. 162+ */ 163+ TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS); 164+ } else 165+#endif 166+ { 167+ MissingRequired(tif, 168+ isTiled(tif) ? "TileOffsets" : "StripOffsets"); 169+ goto bad; 170+ } 171+ } 172+ 173+ if( tif->tif_mode == O_RDWR && 174+ tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 && 175+ tif->tif_dir.td_stripoffset_entry.tdir_count == 0 && 176+ tif->tif_dir.td_stripoffset_entry.tdir_type == 0 && 177+ tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 && 178+ tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 && 179+ tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 && 180+ tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 && 181+ tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0 ) 182+ { 183+ /* Directory typically created with TIFFDeferStrileArrayWriting() */ 184+ TIFFSetupStrips(tif); 185+ } 186+ else if( !(tif->tif_flags&TIFF_DEFERSTRILELOAD) ) 187+ { 188+ if( tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 ) 189+ { 190+ if (!TIFFFetchStripThing(tif,&(tif->tif_dir.td_stripoffset_entry), 191+ tif->tif_dir.td_nstrips, 192+ &tif->tif_dir.td_stripoffset_p)) 193+ { 194+ goto bad; 195+ } 196+ } 197+ if( tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 ) 198+ { 199+ if (!TIFFFetchStripThing(tif,&(tif->tif_dir.td_stripbytecount_entry), 200+ tif->tif_dir.td_nstrips, 201+ &tif->tif_dir.td_stripbytecount_p)) 202+ { 203+ goto bad; 204+ } 205+ } 206+ } 207+ 208 /* 209 * Make sure all non-color channels are extrasamples. 210 * If it's not the case, define them as such. 211