diff options
Diffstat (limited to 'src/libtiff/tif_dirread.c')
-rw-r--r-- | src/libtiff/tif_dirread.c | 858 |
1 files changed, 539 insertions, 319 deletions
diff --git a/src/libtiff/tif_dirread.c b/src/libtiff/tif_dirread.c index 5c8c708..22f675a 100644 --- a/src/libtiff/tif_dirread.c +++ b/src/libtiff/tif_dirread.c @@ -1,4 +1,4 @@ -/* $Id: tif_dirread.c,v 1.1 2008/10/17 06:16:07 scuri Exp $ */ +/* $Id: tif_dirread.c,v 1.2 2009/08/21 04:01:59 scuri Exp $ */ /* * Copyright (c) 1988-1997 Sam Leffler @@ -41,9 +41,13 @@ extern void TIFFCvtIEEEFloatToNative(TIFF*, uint32, float*); extern void TIFFCvtIEEEDoubleToNative(TIFF*, uint32, double*); #endif +static TIFFDirEntry* TIFFReadDirectoryFind(TIFFDirEntry* dir, + uint16 dircount, uint16 tagid); static int EstimateStripByteCounts(TIFF*, TIFFDirEntry*, uint16); static void MissingRequired(TIFF*, const char*); +static int TIFFCheckDirOffset(TIFF*, toff_t); static int CheckDirCount(TIFF*, TIFFDirEntry*, uint32); +static uint16 TIFFFetchDirectory(TIFF*, toff_t, TIFFDirEntry**, toff_t *); static tsize_t TIFFFetchData(TIFF*, TIFFDirEntry*, char*); static tsize_t TIFFFetchString(TIFF*, TIFFDirEntry*, char*); static float TIFFFetchRational(TIFF*, TIFFDirEntry*); @@ -54,6 +58,7 @@ static int TIFFFetchPerSampleAnys(TIFF*, TIFFDirEntry*, double*); static int TIFFFetchShortArray(TIFF*, TIFFDirEntry*, uint16*); static int TIFFFetchStripThing(TIFF*, TIFFDirEntry*, long, uint32**); static int TIFFFetchRefBlackWhite(TIFF*, TIFFDirEntry*); +static int TIFFFetchSubjectDistance(TIFF*, TIFFDirEntry*); static float TIFFFetchFloat(TIFF*, TIFFDirEntry*); static int TIFFFetchFloatArray(TIFF*, TIFFDirEntry*, float*); static int TIFFFetchDoubleArray(TIFF*, TIFFDirEntry*, double*); @@ -62,9 +67,8 @@ static int TIFFFetchShortPair(TIFF*, TIFFDirEntry*); static void ChopUpSingleUncompressedStrip(TIFF*); /* - * Read the next TIFF directory from a file - * and convert it to the internal format. - * We read directories sequentially. + * Read the next TIFF directory from a file and convert it to the internal + * format. We read directories sequentially. */ int TIFFReadDirectory(TIFF* tif) @@ -79,105 +83,27 @@ TIFFReadDirectory(TIFF* tif) const TIFFFieldInfo* fip; size_t fix; uint16 dircount; - toff_t nextdiroff; - int diroutoforderwarning = 0; - toff_t* new_dirlist; + int diroutoforderwarning = 0, compressionknown = 0; tif->tif_diroff = tif->tif_nextdiroff; - if (tif->tif_diroff == 0) /* no more directories */ - return (0); - /* - * XXX: 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 this list. + * Check whether we have the last offset or bad offset (IFD looping). */ - for (n = 0; n < tif->tif_dirnumber; n++) { - if (tif->tif_dirlist[n] == tif->tif_diroff) - return (0); - } - tif->tif_dirnumber++; - new_dirlist = (toff_t *)_TIFFrealloc(tif->tif_dirlist, - tif->tif_dirnumber * sizeof(toff_t)); - if (!new_dirlist) { - TIFFErrorExt(tif->tif_clientdata, module, - "%s: Failed to allocate space for IFD list", - tif->tif_name); - return (0); - } - tif->tif_dirlist = new_dirlist; - tif->tif_dirlist[tif->tif_dirnumber - 1] = tif->tif_diroff; - + if (!TIFFCheckDirOffset(tif, tif->tif_nextdiroff)) + return 0; /* * Cleanup any previous compression state. */ (*tif->tif_cleanup)(tif); tif->tif_curdir++; - nextdiroff = 0; - if (!isMapped(tif)) { - if (!SeekOK(tif, tif->tif_diroff)) { - TIFFErrorExt(tif->tif_clientdata, module, - "%s: Seek error accessing TIFF directory", - tif->tif_name); - return (0); - } - if (!ReadOK(tif, &dircount, sizeof (uint16))) { - TIFFErrorExt(tif->tif_clientdata, module, - "%s: Can not read TIFF directory count", - tif->tif_name); - return (0); - } - if (tif->tif_flags & TIFF_SWAB) - TIFFSwabShort(&dircount); - dir = (TIFFDirEntry *)_TIFFCheckMalloc(tif, dircount, - sizeof (TIFFDirEntry), - "to read TIFF directory"); - if (dir == NULL) - return (0); - if (!ReadOK(tif, dir, dircount*sizeof (TIFFDirEntry))) { - TIFFErrorExt(tif->tif_clientdata, module, - "%.100s: Can not read TIFF directory", - tif->tif_name); - goto bad; - } - /* - * Read offset to next directory for sequential scans. - */ - (void) ReadOK(tif, &nextdiroff, sizeof (uint32)); - } else { - toff_t off = tif->tif_diroff; - - if (off + sizeof (uint16) > tif->tif_size) { - TIFFErrorExt(tif->tif_clientdata, module, - "%s: Can not read TIFF directory count", - tif->tif_name); - return (0); - } else - _TIFFmemcpy(&dircount, tif->tif_base + off, sizeof (uint16)); - off += sizeof (uint16); - if (tif->tif_flags & TIFF_SWAB) - TIFFSwabShort(&dircount); - dir = (TIFFDirEntry *)_TIFFCheckMalloc(tif, dircount, - sizeof (TIFFDirEntry), - "to read TIFF directory"); - if (dir == NULL) - return (0); - if (off + dircount*sizeof (TIFFDirEntry) > tif->tif_size) { - TIFFErrorExt(tif->tif_clientdata, module, - "%s: Can not read TIFF directory", - tif->tif_name); - goto bad; - } else { - _TIFFmemcpy(dir, tif->tif_base + off, - dircount*sizeof (TIFFDirEntry)); - } - off += dircount* sizeof (TIFFDirEntry); - if (off + sizeof (uint32) <= tif->tif_size) - _TIFFmemcpy(&nextdiroff, tif->tif_base+off, sizeof (uint32)); + dircount = TIFFFetchDirectory(tif, tif->tif_nextdiroff, + &dir, &tif->tif_nextdiroff); + if (!dircount) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Failed to read directory at offset %u", + tif->tif_name, tif->tif_nextdiroff); + return 0; } - if (tif->tif_flags & TIFF_SWAB) - TIFFSwabLong(&nextdiroff); - tif->tif_nextdiroff = nextdiroff; tif->tif_flags &= ~TIFF_BEENWRITING; /* reset before new dir */ /* @@ -216,7 +142,7 @@ TIFFReadDirectory(TIFF* tif) * * It sure would have been nice if Aldus had really thought * this stuff through carefully. - */ + */ for (dp = dir, n = dircount; n > 0; n--, dp++) { if (tif->tif_flags & TIFF_SWAB) { TIFFSwabArrayOfShort(&dp->tdir_tag, 2); @@ -236,7 +162,7 @@ TIFFReadDirectory(TIFF* tif) if (fix >= tif->tif_nfields || dp->tdir_tag == IGNORE) continue; - + /* * Silicon Beach (at least) writes unordered * directory tags (violating the spec). Handle @@ -246,13 +172,13 @@ TIFFReadDirectory(TIFF* tif) if (!diroutoforderwarning) { TIFFWarningExt(tif->tif_clientdata, module, "%s: invalid TIFF directory; tags are not sorted in ascending order", - tif->tif_name); + tif->tif_name); diroutoforderwarning = 1; } fix = 0; /* O(n^2) */ } while (fix < tif->tif_nfields && - tif->tif_fieldinfo[fix]->field_tag < dp->tdir_tag) + tif->tif_fieldinfo[fix]->field_tag < dp->tdir_tag) fix++; if (fix >= tif->tif_nfields || tif->tif_fieldinfo[fix]->field_tag != dp->tdir_tag) { @@ -262,18 +188,25 @@ TIFFReadDirectory(TIFF* tif) "%s: unknown field with tag %d (0x%x) encountered", tif->tif_name, dp->tdir_tag, - dp->tdir_tag, - dp->tdir_type); + dp->tdir_tag); - TIFFMergeFieldInfo(tif, - _TIFFCreateAnonFieldInfo(tif, + if (!_TIFFMergeFieldInfo(tif, + _TIFFCreateAnonFieldInfo(tif, dp->tdir_tag, (TIFFDataType) dp->tdir_type), - 1 ); - fix = 0; - while (fix < tif->tif_nfields && - tif->tif_fieldinfo[fix]->field_tag < dp->tdir_tag) - fix++; + 1)) + { + TIFFWarningExt(tif->tif_clientdata, + module, + "Registering anonymous field with tag %d (0x%x) failed", + dp->tdir_tag, + dp->tdir_tag); + goto ignore; + } + fix = 0; + while (fix < tif->tif_nfields && + tif->tif_fieldinfo[fix]->field_tag < dp->tdir_tag) + fix++; } /* * Null out old tags that we ignore. @@ -288,10 +221,10 @@ TIFFReadDirectory(TIFF* tif) */ fip = tif->tif_fieldinfo[fix]; while (dp->tdir_type != (unsigned short) fip->field_type - && fix < tif->tif_nfields) { + && fix < tif->tif_nfields) { if (fip->field_type == TIFF_ANY) /* wildcard */ break; - fip = tif->tif_fieldinfo[++fix]; + fip = tif->tif_fieldinfo[++fix]; if (fix >= tif->tif_nfields || fip->field_tag != dp->tdir_tag) { TIFFWarningExt(tif->tif_clientdata, module, @@ -326,6 +259,8 @@ TIFFReadDirectory(TIFF* tif) dp->tdir_type, dp->tdir_offset); if (!TIFFSetField(tif, dp->tdir_tag, (uint16)v)) goto bad; + else + compressionknown = 1; break; /* XXX: workaround for broken TIFFs */ } else if (dp->tdir_type == TIFF_LONG) { @@ -362,6 +297,30 @@ TIFFReadDirectory(TIFF* tif) } /* + * XXX: OJPEG hack. + * If a) compression is OJPEG, b) planarconfig tag says it's separate, + * c) strip offsets/bytecounts tag are both present and + * d) both contain exactly one value, then we consistently find + * that the buggy implementation of the buggy compression scheme + * matches contig planarconfig best. So we 'fix-up' the tag here + */ + if ((td->td_compression==COMPRESSION_OJPEG) && + (td->td_planarconfig==PLANARCONFIG_SEPARATE)) { + dp = TIFFReadDirectoryFind(dir,dircount,TIFFTAG_STRIPOFFSETS); + if ((dp!=0) && (dp->tdir_count==1)) { + dp = TIFFReadDirectoryFind(dir, dircount, + TIFFTAG_STRIPBYTECOUNTS); + if ((dp!=0) && (dp->tdir_count==1)) { + td->td_planarconfig=PLANARCONFIG_CONTIG; + TIFFWarningExt(tif->tif_clientdata, + "TIFFReadDirectory", + "Planarconfig tag value assumed incorrect, " + "assuming data is contig instead of chunky"); + } + } + } + + /* * Allocate directory structure and setup defaults. */ if (!TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS)) { @@ -369,7 +328,7 @@ TIFFReadDirectory(TIFF* tif) goto bad; } /* - * Setup appropriate structures (by strip or by tile) + * Setup appropriate structures (by strip or by tile) */ if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) { td->td_nstrips = TIFFNumberOfStrips(tif); @@ -391,9 +350,23 @@ TIFFReadDirectory(TIFF* tif) if (td->td_planarconfig == PLANARCONFIG_SEPARATE) td->td_stripsperimage /= td->td_samplesperpixel; if (!TIFFFieldSet(tif, FIELD_STRIPOFFSETS)) { - MissingRequired(tif, + if ((td->td_compression==COMPRESSION_OJPEG) && + (isTiled(tif)==0) && + (td->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 { + MissingRequired(tif, isTiled(tif) ? "TileOffsets" : "StripOffsets"); - goto bad; + goto bad; + } } /* @@ -414,11 +387,11 @@ TIFFReadDirectory(TIFF* tif) * one value per sample. Because of this, we * accept the tag if one value is supplied. * - * The MinSampleValue, MaxSampleValue, BitsPerSample - * DataType and SampleFormat tags are supposed to be - * written as one value/sample, but some vendors - * incorrectly write one value only -- so we accept - * that as well (yech). Other vendors write correct + * The MinSampleValue, MaxSampleValue, BitsPerSample + * DataType and SampleFormat tags are supposed to be + * written as one value/sample, but some vendors + * incorrectly write one value only -- so we accept + * that as well (yech). Other vendors write correct * value for NumberOfSamples, but incorrect one for * BitsPerSample and friends, and we will read this * too. @@ -529,6 +502,69 @@ TIFFReadDirectory(TIFF* tif) } } /* + * OJPEG hack: + * - If a) compression is OJPEG, and b) photometric tag is missing, + * then we consistently find that photometric should be YCbCr + * - If a) compression is OJPEG, and b) photometric tag says it's RGB, + * then we consistently find that the buggy implementation of the + * buggy compression scheme matches photometric YCbCr instead. + * - If a) compression is OJPEG, and b) bitspersample tag is missing, + * then we consistently find bitspersample should be 8. + * - If a) compression is OJPEG, b) samplesperpixel tag is missing, + * and c) photometric is RGB or YCbCr, then we consistently find + * samplesperpixel should be 3 + * - If a) compression is OJPEG, b) samplesperpixel tag is missing, + * and c) photometric is MINISWHITE or MINISBLACK, then we consistently + * find samplesperpixel should be 3 + */ + if (td->td_compression==COMPRESSION_OJPEG) + { + if (!TIFFFieldSet(tif,FIELD_PHOTOMETRIC)) + { + TIFFWarningExt(tif->tif_clientdata, "TIFFReadDirectory", + "Photometric tag is missing, assuming data is YCbCr"); + if (!TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,PHOTOMETRIC_YCBCR)) + goto bad; + } + else if (td->td_photometric==PHOTOMETRIC_RGB) + { + td->td_photometric=PHOTOMETRIC_YCBCR; + TIFFWarningExt(tif->tif_clientdata, "TIFFReadDirectory", + "Photometric tag value assumed incorrect, " + "assuming data is YCbCr instead of RGB"); + } + if (!TIFFFieldSet(tif,FIELD_BITSPERSAMPLE)) + { + TIFFWarningExt(tif->tif_clientdata,"TIFFReadDirectory", + "BitsPerSample tag is missing, assuming 8 bits per sample"); + if (!TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,8)) + goto bad; + } + if (!TIFFFieldSet(tif,FIELD_SAMPLESPERPIXEL)) + { + if ((td->td_photometric==PHOTOMETRIC_RGB) + || (td->td_photometric==PHOTOMETRIC_YCBCR)) + { + TIFFWarningExt(tif->tif_clientdata, + "TIFFReadDirectory", + "SamplesPerPixel tag is missing, " + "assuming correct SamplesPerPixel value is 3"); + if (!TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,3)) + goto bad; + } + else if ((td->td_photometric==PHOTOMETRIC_MINISWHITE) + || (td->td_photometric==PHOTOMETRIC_MINISBLACK)) + { + TIFFWarningExt(tif->tif_clientdata, + "TIFFReadDirectory", + "SamplesPerPixel tag is missing, " + "assuming correct SamplesPerPixel value is 1"); + if (!TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,1)) + goto bad; + } + } + } + /* * Verify Palette image has a Colormap. */ if (td->td_photometric == PHOTOMETRIC_PALETTE && @@ -537,76 +573,90 @@ TIFFReadDirectory(TIFF* tif) goto bad; } /* - * Attempt to deal with a missing StripByteCounts tag. + * OJPEG hack: + * We do no further messing with strip/tile offsets/bytecounts in OJPEG + * TIFFs */ - if (!TIFFFieldSet(tif, FIELD_STRIPBYTECOUNTS)) { + if (td->td_compression!=COMPRESSION_OJPEG) + { /* - * Some manufacturers violate the spec by not giving - * the size of the strips. In this case, assume there - * is one uncompressed strip of data. + * Attempt to deal with a missing StripByteCounts tag. */ - if ((td->td_planarconfig == PLANARCONFIG_CONTIG && - td->td_nstrips > 1) || - (td->td_planarconfig == PLANARCONFIG_SEPARATE && - td->td_nstrips != td->td_samplesperpixel)) { - MissingRequired(tif, "StripByteCounts"); - goto bad; - } - TIFFWarningExt(tif->tif_clientdata, module, - "%s: TIFF directory is missing required " - "\"%s\" field, calculating from imagelength", - tif->tif_name, - _TIFFFieldWithTag(tif,TIFFTAG_STRIPBYTECOUNTS)->field_name); - if (EstimateStripByteCounts(tif, dir, dircount) < 0) - goto bad; -/* - * Assume we have wrong StripByteCount value (in case of single strip) in - * following cases: - * - it is equal to zero along with StripOffset; - * - it is larger than file itself (in case of uncompressed image); - * - it is smaller than the size of the bytes per row multiplied on the - * number of rows. The last case should not be checked in the case of - * writing new image, because we may do not know the exact strip size - * until the whole image will be written and directory dumped out. - */ -#define BYTECOUNTLOOKSBAD \ - ( (td->td_stripbytecount[0] == 0 && td->td_stripoffset[0] != 0) || \ - (td->td_compression == COMPRESSION_NONE && \ - td->td_stripbytecount[0] > TIFFGetFileSize(tif) - td->td_stripoffset[0]) || \ - (tif->tif_mode == O_RDONLY && \ - td->td_compression == COMPRESSION_NONE && \ - td->td_stripbytecount[0] < TIFFScanlineSize(tif) * td->td_imagelength) ) - - } else if (td->td_nstrips == 1 - && td->td_stripoffset[0] != 0 - && BYTECOUNTLOOKSBAD) { + if (!TIFFFieldSet(tif, FIELD_STRIPBYTECOUNTS)) { + /* + * Some manufacturers violate the spec by not giving + * the size of the strips. In this case, assume there + * is one uncompressed strip of data. + */ + if ((td->td_planarconfig == PLANARCONFIG_CONTIG && + td->td_nstrips > 1) || + (td->td_planarconfig == PLANARCONFIG_SEPARATE && + td->td_nstrips != td->td_samplesperpixel)) { + MissingRequired(tif, "StripByteCounts"); + goto bad; + } + TIFFWarningExt(tif->tif_clientdata, module, + "%s: TIFF directory is missing required " + "\"%s\" field, calculating from imagelength", + tif->tif_name, + _TIFFFieldWithTag(tif,TIFFTAG_STRIPBYTECOUNTS)->field_name); + if (EstimateStripByteCounts(tif, dir, dircount) < 0) + goto bad; /* - * XXX: Plexus (and others) sometimes give a value of zero for - * a tag when they don't know what the correct value is! Try - * and handle the simple case of estimating the size of a one - * strip image. + * Assume we have wrong StripByteCount value (in case + * of single strip) in following cases: + * - it is equal to zero along with StripOffset; + * - it is larger than file itself (in case of uncompressed + * image); + * - it is smaller than the size of the bytes per row + * multiplied on the number of rows. The last case should + * not be checked in the case of writing new image, + * because we may do not know the exact strip size + * until the whole image will be written and directory + * dumped out. */ - TIFFWarningExt(tif->tif_clientdata, module, + #define BYTECOUNTLOOKSBAD \ + ( (td->td_stripbytecount[0] == 0 && td->td_stripoffset[0] != 0) || \ + (td->td_compression == COMPRESSION_NONE && \ + td->td_stripbytecount[0] > TIFFGetFileSize(tif) - td->td_stripoffset[0]) || \ + (tif->tif_mode == O_RDONLY && \ + td->td_compression == COMPRESSION_NONE && \ + td->td_stripbytecount[0] < TIFFScanlineSize(tif) * td->td_imagelength) ) + + } else if (td->td_nstrips == 1 + && td->td_stripoffset[0] != 0 + && BYTECOUNTLOOKSBAD) { + /* + * XXX: Plexus (and others) sometimes give a value of + * zero for a tag when they don't know what the + * correct value is! Try and handle the simple case + * of estimating the size of a one strip image. + */ + TIFFWarningExt(tif->tif_clientdata, module, "%s: Bogus \"%s\" field, ignoring and calculating from imagelength", - tif->tif_name, - _TIFFFieldWithTag(tif,TIFFTAG_STRIPBYTECOUNTS)->field_name); - if(EstimateStripByteCounts(tif, dir, dircount) < 0) - goto bad; - } else if (td->td_planarconfig == PLANARCONFIG_CONTIG - && td->td_nstrips > 2 - && td->td_compression == COMPRESSION_NONE - && td->td_stripbytecount[0] != td->td_stripbytecount[1]) { - /* - * XXX: Some vendors fill StripByteCount array with absolutely - * wrong values (it can be equal to StripOffset array, for - * example). Catch this case here. - */ - TIFFWarningExt(tif->tif_clientdata, module, + tif->tif_name, + _TIFFFieldWithTag(tif,TIFFTAG_STRIPBYTECOUNTS)->field_name); + if(EstimateStripByteCounts(tif, dir, dircount) < 0) + goto bad; + } else if (td->td_planarconfig == PLANARCONFIG_CONTIG + && td->td_nstrips > 2 + && td->td_compression == COMPRESSION_NONE + && td->td_stripbytecount[0] != td->td_stripbytecount[1] + && td->td_stripbytecount[0] != 0 + && td->td_stripbytecount[1] != 0 ) { + /* + * XXX: Some vendors fill StripByteCount array with + * absolutely wrong values (it can be equal to + * StripOffset array, for example). Catch this case + * here. + */ + TIFFWarningExt(tif->tif_clientdata, module, "%s: Wrong \"%s\" field, ignoring and calculating from imagelength", - tif->tif_name, - _TIFFFieldWithTag(tif,TIFFTAG_STRIPBYTECOUNTS)->field_name); - if (EstimateStripByteCounts(tif, dir, dircount) < 0) - goto bad; + tif->tif_name, + _TIFFFieldWithTag(tif,TIFFTAG_STRIPBYTECOUNTS)->field_name); + if (EstimateStripByteCounts(tif, dir, dircount) < 0) + goto bad; + } } if (dir) { _TIFFfree((char *)dir); @@ -638,15 +688,15 @@ TIFFReadDirectory(TIFF* tif) if (!TIFFFieldSet(tif, FIELD_COMPRESSION)) TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); - /* - * Some manufacturers make life difficult by writing + /* + * Some manufacturers make life difficult by writing * large amounts of uncompressed data as a single strip. * This is contrary to the recommendations of the spec. - * The following makes an attempt at breaking such images + * The following makes an attempt at breaking such images * into strips closer to the recommended 8k bytes. A * side effect, however, is that the RowsPerStrip tag * value may be changed. - */ + */ if (td->td_nstrips == 1 && td->td_compression == COMPRESSION_NONE && (tif->tif_flags & (TIFF_STRIPCHOP|TIFF_ISTILED)) == TIFF_STRIPCHOP) ChopUpSingleUncompressedStrip(tif); @@ -662,22 +712,25 @@ TIFFReadDirectory(TIFF* tif) tif->tif_scanlinesize = TIFFScanlineSize(tif); if (!tif->tif_scanlinesize) { - TIFFErrorExt(tif->tif_clientdata, module, "%s: cannot handle zero scanline size", - tif->tif_name); + TIFFErrorExt(tif->tif_clientdata, module, + "%s: cannot handle zero scanline size", + tif->tif_name); return (0); } if (isTiled(tif)) { tif->tif_tilesize = TIFFTileSize(tif); if (!tif->tif_tilesize) { - TIFFErrorExt(tif->tif_clientdata, module, "%s: cannot handle zero tile size", - tif->tif_name); + TIFFErrorExt(tif->tif_clientdata, module, + "%s: cannot handle zero tile size", + tif->tif_name); return (0); } } else { if (!TIFFStripSize(tif)) { - TIFFErrorExt(tif->tif_clientdata, module, "%s: cannot handle zero strip size", - tif->tif_name); + TIFFErrorExt(tif->tif_clientdata, module, + "%s: cannot handle zero strip size", + tif->tif_name); return (0); } } @@ -688,7 +741,20 @@ bad: return (0); } -/* +static TIFFDirEntry* +TIFFReadDirectoryFind(TIFFDirEntry* dir, uint16 dircount, uint16 tagid) +{ + TIFFDirEntry* m; + uint16 n; + for (m=dir, n=0; n<dircount; m++, n++) + { + if (m->tdir_tag==tagid) + return(m); + } + return(0); +} + +/* * Read custom directory from the arbitarry offset. * The code is very similar to TIFFReadDirectory(). */ @@ -706,64 +772,16 @@ TIFFReadCustomDirectory(TIFF* tif, toff_t diroff, _TIFFSetupFieldInfo(tif, info, n); - tif->tif_diroff = diroff; - - if (!isMapped(tif)) { - if (!SeekOK(tif, diroff)) { - TIFFErrorExt(tif->tif_clientdata, module, - "%s: Seek error accessing TIFF directory", - tif->tif_name); - return (0); - } - if (!ReadOK(tif, &dircount, sizeof (uint16))) { - TIFFErrorExt(tif->tif_clientdata, module, - "%s: Can not read TIFF directory count", - tif->tif_name); - return (0); - } - if (tif->tif_flags & TIFF_SWAB) - TIFFSwabShort(&dircount); - dir = (TIFFDirEntry *)_TIFFCheckMalloc(tif, dircount, - sizeof (TIFFDirEntry), - "to read TIFF custom directory"); - if (dir == NULL) - return (0); - if (!ReadOK(tif, dir, dircount * sizeof (TIFFDirEntry))) { - TIFFErrorExt(tif->tif_clientdata, module, - "%.100s: Can not read TIFF directory", - tif->tif_name); - goto bad; - } - } else { - toff_t off = diroff; - - if (off + sizeof (uint16) > tif->tif_size) { - TIFFErrorExt(tif->tif_clientdata, module, - "%s: Can not read TIFF directory count", - tif->tif_name); - return (0); - } else - _TIFFmemcpy(&dircount, tif->tif_base + off, sizeof (uint16)); - off += sizeof (uint16); - if (tif->tif_flags & TIFF_SWAB) - TIFFSwabShort(&dircount); - dir = (TIFFDirEntry *)_TIFFCheckMalloc(tif, dircount, - sizeof (TIFFDirEntry), - "to read TIFF custom directory"); - if (dir == NULL) - return (0); - if (off + dircount * sizeof (TIFFDirEntry) > tif->tif_size) { - TIFFErrorExt(tif->tif_clientdata, module, - "%s: Can not read TIFF directory", - tif->tif_name); - goto bad; - } else { - _TIFFmemcpy(dir, tif->tif_base + off, - dircount * sizeof (TIFFDirEntry)); - } + dircount = TIFFFetchDirectory(tif, diroff, &dir, NULL); + if (!dircount) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Failed to read custom directory at offset %u", + tif->tif_name, diroff); + return 0; } TIFFFreeDirectory(tif); + _TIFFmemset(&tif->tif_dir, 0, sizeof(TIFFDirectory)); fix = 0; for (dp = dir, i = dircount; i > 0; i--, dp++) { @@ -784,14 +802,18 @@ TIFFReadCustomDirectory(TIFF* tif, toff_t diroff, TIFFWarningExt(tif->tif_clientdata, module, "%s: unknown field with tag %d (0x%x) encountered", - tif->tif_name, dp->tdir_tag, dp->tdir_tag, - dp->tdir_type); - - TIFFMergeFieldInfo(tif, - _TIFFCreateAnonFieldInfo(tif, - dp->tdir_tag, - (TIFFDataType)dp->tdir_type), - 1); + tif->tif_name, dp->tdir_tag, dp->tdir_tag); + if (!_TIFFMergeFieldInfo(tif, + _TIFFCreateAnonFieldInfo(tif, + dp->tdir_tag, + (TIFFDataType) dp->tdir_type), + 1)) + { + TIFFWarningExt(tif->tif_clientdata, module, + "Registering anonymous field with tag %d (0x%x) failed", + dp->tdir_tag, dp->tdir_tag); + goto ignore; + } fix = 0; while (fix < tif->tif_nfields && @@ -836,17 +858,22 @@ TIFFReadCustomDirectory(TIFF* tif, toff_t diroff, goto ignore; } - (void) TIFFFetchNormalTag(tif, dp); + /* + * EXIF tags which need to be specifically processed. + */ + switch (dp->tdir_tag) { + case EXIFTAG_SUBJECTDISTANCE: + (void) TIFFFetchSubjectDistance(tif, dp); + break; + default: + (void) TIFFFetchNormalTag(tif, dp); + break; + } } if (dir) _TIFFfree(dir); return 1; - -bad: - if (dir) - _TIFFfree(dir); - return 0; } /* @@ -868,15 +895,18 @@ EstimateStripByteCounts(TIFF* tif, TIFFDirEntry* dir, uint16 dircount) { static const char module[] = "EstimateStripByteCounts"; - register TIFFDirEntry *dp; - register TIFFDirectory *td = &tif->tif_dir; - uint16 i; + TIFFDirEntry *dp; + TIFFDirectory *td = &tif->tif_dir; + uint32 strip; if (td->td_stripbytecount) _TIFFfree(td->td_stripbytecount); td->td_stripbytecount = (uint32*) _TIFFCheckMalloc(tif, td->td_nstrips, sizeof (uint32), "for \"StripByteCounts\" array"); + if( td->td_stripbytecount == NULL ) + return -1; + if (td->td_compression != COMPRESSION_NONE) { uint32 space = (uint32)(sizeof (TIFFHeader) + sizeof (uint16) @@ -902,8 +932,8 @@ EstimateStripByteCounts(TIFF* tif, TIFFDirEntry* dir, uint16 dircount) space = filesize - space; if (td->td_planarconfig == PLANARCONFIG_SEPARATE) space /= td->td_samplesperpixel; - for (i = 0; i < td->td_nstrips; i++) - td->td_stripbytecount[i] = space; + for (strip = 0; strip < td->td_nstrips; strip++) + td->td_stripbytecount[strip] = space; /* * This gross hack handles the case were the offset to * the last strip is past the place where we think the strip @@ -911,16 +941,21 @@ EstimateStripByteCounts(TIFF* tif, TIFFDirEntry* dir, uint16 dircount) * it's safe to assume that we've overestimated the amount * of data in the strip and trim this number back accordingly. */ - i--; - if (((toff_t)(td->td_stripoffset[i]+td->td_stripbytecount[i])) - > filesize) - td->td_stripbytecount[i] = - filesize - td->td_stripoffset[i]; + strip--; + if (((toff_t)(td->td_stripoffset[strip]+ + td->td_stripbytecount[strip])) > filesize) + td->td_stripbytecount[strip] = + filesize - td->td_stripoffset[strip]; + } else if (isTiled(tif)) { + uint32 bytespertile = TIFFTileSize(tif); + + for (strip = 0; strip < td->td_nstrips; strip++) + td->td_stripbytecount[strip] = bytespertile; } else { uint32 rowbytes = TIFFScanlineSize(tif); uint32 rowsperstrip = td->td_imagelength/td->td_stripsperimage; - for (i = 0; i < td->td_nstrips; i++) - td->td_stripbytecount[i] = rowbytes*rowsperstrip; + for (strip = 0; strip < td->td_nstrips; strip++) + td->td_stripbytecount[strip] = rowbytes * rowsperstrip; } TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS); if (!TIFFFieldSet(tif, FIELD_ROWSPERSTRIP)) @@ -939,23 +974,65 @@ MissingRequired(TIFF* tif, const char* tagname) } /* - * Check the count field of a directory - * entry against a known value. The caller - * is expected to skip/ignore the tag if - * there is a mismatch. + * 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. + */ +static int +TIFFCheckDirOffset(TIFF* tif, toff_t diroff) +{ + uint16 n; + + if (diroff == 0) /* no more directories */ + return 0; + + for (n = 0; n < tif->tif_dirnumber && tif->tif_dirlist; n++) { + if (tif->tif_dirlist[n] == diroff) + return 0; + } + + tif->tif_dirnumber++; + + if (tif->tif_dirnumber > tif->tif_dirlistsize) { + toff_t* new_dirlist; + + /* + * XXX: Reduce memory allocation granularity of the dirlist + * array. + */ + new_dirlist = (toff_t *)_TIFFCheckRealloc(tif, + tif->tif_dirlist, + tif->tif_dirnumber, + 2 * sizeof(toff_t), + "for IFD list"); + if (!new_dirlist) + return 0; + tif->tif_dirlistsize = 2 * tif->tif_dirnumber; + tif->tif_dirlist = new_dirlist; + } + + tif->tif_dirlist[tif->tif_dirnumber - 1] = diroff; + + return 1; +} + +/* + * Check the count field of a directory entry against a known value. The + * caller is expected to skip/ignore the tag if there is a mismatch. */ static int CheckDirCount(TIFF* tif, TIFFDirEntry* dir, uint32 count) { if (count > dir->tdir_count) { TIFFWarningExt(tif->tif_clientdata, tif->tif_name, - "incorrect count for field \"%s\" (%lu, expecting %lu); tag ignored", + "incorrect count for field \"%s\" (%u, expecting %u); tag ignored", _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name, dir->tdir_count, count); return (0); } else if (count < dir->tdir_count) { TIFFWarningExt(tif->tif_clientdata, tif->tif_name, - "incorrect count for field \"%s\" (%lu, expecting %lu); tag trimmed", + "incorrect count for field \"%s\" (%u, expecting %u); tag trimmed", _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name, dir->tdir_count, count); return (1); @@ -964,16 +1041,128 @@ CheckDirCount(TIFF* tif, TIFFDirEntry* dir, uint32 count) } /* + * Read IFD structure from the specified offset. If the pointer to + * nextdiroff variable has been specified, read it too. Function returns a + * number of fields in the directory or 0 if failed. + */ +static uint16 +TIFFFetchDirectory(TIFF* tif, toff_t diroff, TIFFDirEntry **pdir, + toff_t *nextdiroff) +{ + static const char module[] = "TIFFFetchDirectory"; + + TIFFDirEntry *dir; + uint16 dircount; + + assert(pdir); + + tif->tif_diroff = diroff; + if (nextdiroff) + *nextdiroff = 0; + if (!isMapped(tif)) { + if (!SeekOK(tif, tif->tif_diroff)) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Seek error accessing TIFF directory", + tif->tif_name); + return 0; + } + if (!ReadOK(tif, &dircount, sizeof (uint16))) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Can not read TIFF directory count", + tif->tif_name); + return 0; + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabShort(&dircount); + dir = (TIFFDirEntry *)_TIFFCheckMalloc(tif, dircount, + sizeof (TIFFDirEntry), + "to read TIFF directory"); + if (dir == NULL) + return 0; + if (!ReadOK(tif, dir, dircount*sizeof (TIFFDirEntry))) { + TIFFErrorExt(tif->tif_clientdata, module, + "%.100s: Can not read TIFF directory", + tif->tif_name); + _TIFFfree(dir); + return 0; + } + /* + * Read offset to next directory for sequential scans if + * needed. + */ + if (nextdiroff) + (void) ReadOK(tif, nextdiroff, sizeof(uint32)); + } else { + toff_t off = tif->tif_diroff; + + /* + * Check for integer overflow when validating the dir_off, + * otherwise a very high offset may cause an OOB read and + * crash the client. Make two comparisons instead of + * + * off + sizeof(uint16) > tif->tif_size + * + * to avoid overflow. + */ + if (tif->tif_size < sizeof (uint16) || + off > tif->tif_size - sizeof(uint16)) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Can not read TIFF directory count", + tif->tif_name); + return 0; + } else { + _TIFFmemcpy(&dircount, tif->tif_base + off, + sizeof(uint16)); + } + off += sizeof (uint16); + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabShort(&dircount); + dir = (TIFFDirEntry *)_TIFFCheckMalloc(tif, dircount, + sizeof(TIFFDirEntry), + "to read TIFF directory"); + if (dir == NULL) + return 0; + if (off + dircount * sizeof (TIFFDirEntry) > tif->tif_size) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Can not read TIFF directory", + tif->tif_name); + _TIFFfree(dir); + return 0; + } else { + _TIFFmemcpy(dir, tif->tif_base + off, + dircount * sizeof(TIFFDirEntry)); + } + if (nextdiroff) { + off += dircount * sizeof (TIFFDirEntry); + if (off + sizeof (uint32) <= tif->tif_size) { + _TIFFmemcpy(nextdiroff, tif->tif_base + off, + sizeof (uint32)); + } + } + } + if (nextdiroff && tif->tif_flags & TIFF_SWAB) + TIFFSwabLong(nextdiroff); + *pdir = dir; + return dircount; +} + +/* * Fetch a contiguous directory item. */ static tsize_t TIFFFetchData(TIFF* tif, TIFFDirEntry* dir, char* cp) { - int w = TIFFDataWidth((TIFFDataType) dir->tdir_type); - tsize_t cc = dir->tdir_count * w; + uint32 w = TIFFDataWidth((TIFFDataType) dir->tdir_type); + /* + * FIXME: butecount should have tsize_t type, but for now libtiff + * defines tsize_t as a signed 32-bit integer and we are losing + * ability to read arrays larger than 2^31 bytes. So we are using + * uint32 instead of tsize_t here. + */ + uint32 cc = dir->tdir_count * w; /* Check for overflow. */ - if (!dir->tdir_count || !w || cc / w != (tsize_t)dir->tdir_count) + if (!dir->tdir_count || !w || cc / w != dir->tdir_count) goto bad; if (!isMapped(tif)) { @@ -983,9 +1172,9 @@ TIFFFetchData(TIFF* tif, TIFFDirEntry* dir, char* cp) goto bad; } else { /* Check for overflow. */ - if ((tsize_t)dir->tdir_offset + cc < (tsize_t)dir->tdir_offset - || (tsize_t)dir->tdir_offset + cc < cc - || (tsize_t)dir->tdir_offset + cc > (tsize_t)tif->tif_size) + if (dir->tdir_offset + cc < dir->tdir_offset + || dir->tdir_offset + cc < cc + || dir->tdir_offset + cc > tif->tif_size) goto bad; _TIFFmemcpy(cp, tif->tif_base + dir->tdir_offset, cc); } @@ -1041,7 +1230,7 @@ cvtRational(TIFF* tif, TIFFDirEntry* dir, uint32 num, uint32 denom, float* rv) { if (denom == 0) { TIFFErrorExt(tif->tif_clientdata, tif->tif_name, - "%s: Rational with zero denominator (num = %lu)", + "%s: Rational with zero denominator (num = %u)", _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name, num); return (0); } else { @@ -1054,9 +1243,8 @@ cvtRational(TIFF* tif, TIFFDirEntry* dir, uint32 num, uint32 denom, float* rv) } /* - * Fetch a rational item from the file - * at offset off and return the value - * as a floating point number. + * Fetch a rational item from the file at offset off and return the value as a + * floating point number. */ static float TIFFFetchRational(TIFF* tif, TIFFDirEntry* dir) @@ -1069,9 +1257,8 @@ TIFFFetchRational(TIFF* tif, TIFFDirEntry* dir) } /* - * Fetch a single floating point value - * from the offset field and return it - * as a native float. + * Fetch a single floating point value from the offset field and return it as + * a native float. */ static float TIFFFetchFloat(TIFF* tif, TIFFDirEntry* dir) @@ -1159,6 +1346,18 @@ TIFFFetchShortArray(TIFF* tif, TIFFDirEntry* dir, uint16* v) static int TIFFFetchShortPair(TIFF* tif, TIFFDirEntry* dir) { + /* + * Prevent overflowing the v stack arrays below by performing a sanity + * check on tdir_count, this should never be greater than two. + */ + if (dir->tdir_count > 2) { + TIFFWarningExt(tif->tif_clientdata, tif->tif_name, + "unexpected count for field \"%s\", %u, expected 2; ignored", + _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name, + dir->tdir_count); + return 0; + } + switch (dir->tdir_type) { case TIFF_BYTE: case TIFF_SBYTE: @@ -1251,15 +1450,13 @@ TIFFFetchDoubleArray(TIFF* tif, TIFFDirEntry* dir, double* v) } /* - * Fetch an array of ANY values. The actual values are - * returned as doubles which should be able hold all the - * types. Yes, there really should be an tany_t to avoid - * this potential non-portability ... Note in particular - * that we assume that the double return value vector is - * large enough to read in any fundamental type. We use - * that vector as a buffer to read in the base type vector - * and then convert it in place to double (from end - * to front of course). + * Fetch an array of ANY values. The actual values are returned as doubles + * which should be able hold all the types. Yes, there really should be an + * tany_t to avoid this potential non-portability ... Note in particular that + * we assume that the double return value vector is large enough to read in + * any fundamental type. We use that vector as a buffer to read in the base + * type vector and then convert it in place to double (from end to front of + * course). */ static int TIFFFetchAnyArray(TIFF* tif, TIFFDirEntry* dir, double* v) @@ -1510,10 +1707,10 @@ TIFFFetchPerSampleShorts(TIFF* tif, TIFFDirEntry* dir, uint16* pl) for (i = 1; i < check_count; i++) if (v[i] != v[0]) { - TIFFErrorExt(tif->tif_clientdata, tif->tif_name, - "Cannot handle different per-sample values for field \"%s\"", - _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name); - goto bad; + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "Cannot handle different per-sample values for field \"%s\"", + _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name); + goto bad; } *pl = v[0]; status = 1; @@ -1551,10 +1748,10 @@ TIFFFetchPerSampleLongs(TIFF* tif, TIFFDirEntry* dir, uint32* pl) check_count = samples; for (i = 1; i < check_count; i++) if (v[i] != v[0]) { - TIFFErrorExt(tif->tif_clientdata, tif->tif_name, - "Cannot handle different per-sample values for field \"%s\"", - _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name); - goto bad; + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "Cannot handle different per-sample values for field \"%s\"", + _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name); + goto bad; } *pl = v[0]; status = 1; @@ -1591,10 +1788,10 @@ TIFFFetchPerSampleAnys(TIFF* tif, TIFFDirEntry* dir, double* pl) for (i = 1; i < check_count; i++) if (v[i] != v[0]) { - TIFFErrorExt(tif->tif_clientdata, tif->tif_name, - "Cannot handle different per-sample values for field \"%s\"", - _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name); - goto bad; + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "Cannot handle different per-sample values for field \"%s\"", + _TIFFFieldWithTag(tif, dir->tdir_tag)->field_name); + goto bad; } *pl = v[0]; status = 1; @@ -1706,11 +1903,34 @@ TIFFFetchRefBlackWhite(TIFF* tif, TIFFDirEntry* dir) } /* - * Replace a single strip (tile) of uncompressed data by - * multiple strips (tiles), each approximately 8Kbytes. - * This is useful for dealing with large images or - * for dealing with machines with a limited amount - * memory. + * Fetch and set the SubjectDistance EXIF tag. + */ +static int +TIFFFetchSubjectDistance(TIFF* tif, TIFFDirEntry* dir) +{ + uint32 l[2]; + float v; + int ok = 0; + + if (TIFFFetchData(tif, dir, (char *)l) + && cvtRational(tif, dir, l[0], l[1], &v)) { + /* + * XXX: Numerator 0xFFFFFFFF means that we have infinite + * distance. Indicate that with a negative floating point + * SubjectDistance value. + */ + ok = TIFFSetField(tif, dir->tdir_tag, + (l[0] != 0xFFFFFFFF) ? v : -v); + } + + return ok; +} + +/* + * Replace a single strip (tile) of uncompressed data by multiple strips + * (tiles), each approximately STRIP_SIZE_DEFAULT bytes. This is useful for + * dealing with large images or for dealing with machines with a limited + * amount memory. */ static void ChopUpSingleUncompressedStrip(TIFF* tif) @@ -1752,8 +1972,8 @@ ChopUpSingleUncompressedStrip(TIFF* tif) "for chopped \"StripOffsets\" array"); if (newcounts == NULL || newoffsets == NULL) { /* - * Unable to allocate new strip information, give - * up and use the original one strip information. + * Unable to allocate new strip information, give up and use + * the original one strip information. */ if (newcounts != NULL) _TIFFfree(newcounts); @@ -1766,7 +1986,7 @@ ChopUpSingleUncompressedStrip(TIFF* tif) * that reflect the broken-up format. */ for (strip = 0; strip < nstrips; strip++) { - if (stripbytes > (tsize_t) bytecount) + if ((uint32)stripbytes > bytecount) stripbytes = bytecount; newcounts[strip] = stripbytes; newoffsets[strip] = offset; |