Fix unsafe handling of DotRange and related tags. Back-port of upstream patch for CVE-2012-5581. (Note: I have not pushed this into upstream CVS for the 3.9 branch, because I'm not entirely convinced that it won't create application compatibility issues --- tgl) diff -Naur tiff-3.9.7.orig/libtiff/tif_dir.c tiff-3.9.7/libtiff/tif_dir.c --- tiff-3.9.7.orig/libtiff/tif_dir.c 2012-09-22 10:48:09.000000000 -0400 +++ tiff-3.9.7/libtiff/tif_dir.c 2012-12-13 13:39:20.448864070 -0500 @@ -494,32 +494,28 @@ goto end; } - if ((fip->field_passcount + if (fip->field_tag == TIFFTAG_DOTRANGE + && strcmp(fip->field_name,"DotRange") == 0) { + /* TODO: This is an evil exception and should not have been + handled this way ... likely best if we move it into + the directory structure with an explicit field in + libtiff 4.1 and assign it a FIELD_ value */ + uint16 v[2]; + v[0] = (uint16)va_arg(ap, int); + v[1] = (uint16)va_arg(ap, int); + _TIFFmemcpy(tv->value, v, 4); + } + else if (fip->field_passcount || fip->field_writecount == TIFF_VARIABLE || fip->field_writecount == TIFF_VARIABLE2 || fip->field_writecount == TIFF_SPP - || tv->count > 1) - && fip->field_tag != TIFFTAG_PAGENUMBER - && fip->field_tag != TIFFTAG_HALFTONEHINTS - && fip->field_tag != TIFFTAG_YCBCRSUBSAMPLING - && fip->field_tag != TIFFTAG_DOTRANGE - && fip->field_tag != TIFFTAG_WHITELEVEL) { + || tv->count > 1) { _TIFFmemcpy(tv->value, va_arg(ap, void *), tv->count * tv_size); } else { - /* - * XXX: The following loop required to handle - * TIFFTAG_PAGENUMBER, TIFFTAG_HALFTONEHINTS, - * TIFFTAG_YCBCRSUBSAMPLING and TIFFTAG_DOTRANGE tags. - * These tags are actually arrays and should be passed as - * array pointers to TIFFSetField() function, but actually - * passed as a list of separate values. This behaviour - * must be changed in the future! - */ - int i; char *val = (char *)tv->value; - for (i = 0; i < tv->count; i++, val += tv_size) { + assert( tv->count == 1 ); switch (fip->field_type) { case TIFF_BYTE: case TIFF_UNDEFINED: @@ -578,7 +574,6 @@ status = 0; break; } - } } } } @@ -869,24 +864,27 @@ *va_arg(ap, uint16*) = (uint16)tv->count; *va_arg(ap, void **) = tv->value; ret_val = 1; - } else { - if ((fip->field_type == TIFF_ASCII + } else if (fip->field_tag == TIFFTAG_DOTRANGE + && strcmp(fip->field_name,"DotRange") == 0) { + /* TODO: This is an evil exception and should not have been + handled this way ... likely best if we move it into + the directory structure with an explicit field in + libtiff 4.1 and assign it a FIELD_ value */ + *va_arg(ap, uint16*) = ((uint16 *)tv->value)[0]; + *va_arg(ap, uint16*) = ((uint16 *)tv->value)[1]; + ret_val = 1; + } else { + if (fip->field_type == TIFF_ASCII || fip->field_readcount == TIFF_VARIABLE || fip->field_readcount == TIFF_VARIABLE2 || fip->field_readcount == TIFF_SPP - || tv->count > 1) - && fip->field_tag != TIFFTAG_PAGENUMBER - && fip->field_tag != TIFFTAG_HALFTONEHINTS - && fip->field_tag != TIFFTAG_YCBCRSUBSAMPLING - && fip->field_tag != TIFFTAG_DOTRANGE) { + || tv->count > 1) { *va_arg(ap, void **) = tv->value; ret_val = 1; } else { - int j; char *val = (char *)tv->value; - for (j = 0; j < tv->count; - j++, val += _TIFFDataSize(tv->info->field_type)) { + assert( tv->count == 1 ); switch (fip->field_type) { case TIFF_BYTE: case TIFF_UNDEFINED: @@ -936,7 +934,6 @@ ret_val = 0; break; } - } } } break; diff -Naur tiff-3.9.7.orig/libtiff/tif_print.c tiff-3.9.7/libtiff/tif_print.c --- tiff-3.9.7.orig/libtiff/tif_print.c 2010-07-08 12:17:59.000000000 -0400 +++ tiff-3.9.7/libtiff/tif_print.c 2012-12-13 13:42:12.773478278 -0500 @@ -112,16 +112,22 @@ } static int -_TIFFPrettyPrintField(TIFF* tif, FILE* fd, ttag_t tag, +_TIFFPrettyPrintField(TIFF* tif, const TIFFFieldInfo *fip, FILE* fd, ttag_t tag, uint32 value_count, void *raw_data) { TIFFDirectory *td = &tif->tif_dir; + /* do not try to pretty print auto-defined fields */ + if (strncmp(fip->field_name,"Tag ", 4) == 0) { + return 0; + } + switch (tag) { case TIFFTAG_INKSET: - fprintf(fd, " Ink Set: "); - switch (*((uint16*)raw_data)) { + if (value_count == 2 && fip->field_type == TIFF_SHORT) { + fprintf(fd, " Ink Set: "); + switch (*((uint16*)raw_data)) { case INKSET_CMYK: fprintf(fd, "CMYK\n"); break; @@ -130,11 +136,18 @@ *((uint16*)raw_data), *((uint16*)raw_data)); break; + } + return 1; } - return 1; + return 0; + case TIFFTAG_WHITEPOINT: - fprintf(fd, " White Point: %g-%g\n", - ((float *)raw_data)[0], ((float *)raw_data)[1]); return 1; + if (value_count == 2 && fip->field_type == TIFF_RATIONAL) { + fprintf(fd, " White Point: %g-%g\n", + ((float *)raw_data)[0], ((float *)raw_data)[1]); return 1; + } + return 0; + case TIFFTAG_REFERENCEBLACKWHITE: { uint16 i; @@ -174,10 +187,13 @@ (unsigned long) value_count); return 1; case TIFFTAG_STONITS: - fprintf(fd, - " Sample to Nits conversion factor: %.4e\n", - *((double*)raw_data)); - return 1; + if (value_count == 1 && fip->field_type == TIFF_DOUBLE) { + fprintf(fd, + " Sample to Nits conversion factor: %.4e\n", + *((double*)raw_data)); + return 1; + } + return 0; } return 0; @@ -524,44 +540,28 @@ value_count = td->td_samplesperpixel; else value_count = fip->field_readcount; - if ((fip->field_type == TIFF_ASCII + if (fip->field_tag == TIFFTAG_DOTRANGE + && strcmp(fip->field_name,"DotRange") == 0) { + /* TODO: This is an evil exception and should not have been + handled this way ... likely best if we move it into + the directory structure with an explicit field in + libtiff 4.1 and assign it a FIELD_ value */ + static uint16 dotrange[2]; + raw_data = dotrange; + TIFFGetField(tif, tag, dotrange+0, dotrange+1); + } else if (fip->field_type == TIFF_ASCII || fip->field_readcount == TIFF_VARIABLE || fip->field_readcount == TIFF_VARIABLE2 || fip->field_readcount == TIFF_SPP - || value_count > 1) - && fip->field_tag != TIFFTAG_PAGENUMBER - && fip->field_tag != TIFFTAG_HALFTONEHINTS - && fip->field_tag != TIFFTAG_YCBCRSUBSAMPLING - && fip->field_tag != TIFFTAG_DOTRANGE) { + || value_count > 1) { if(TIFFGetField(tif, tag, &raw_data) != 1) continue; - } else if (fip->field_tag != TIFFTAG_PAGENUMBER - && fip->field_tag != TIFFTAG_HALFTONEHINTS - && fip->field_tag != TIFFTAG_YCBCRSUBSAMPLING - && fip->field_tag != TIFFTAG_DOTRANGE) { - raw_data = _TIFFmalloc( - _TIFFDataSize(fip->field_type) - * value_count); - mem_alloc = 1; - if(TIFFGetField(tif, tag, raw_data) != 1) { - _TIFFfree(raw_data); - continue; - } } else { - /* - * XXX: Should be fixed and removed, see the - * notes related to TIFFTAG_PAGENUMBER, - * TIFFTAG_HALFTONEHINTS, - * TIFFTAG_YCBCRSUBSAMPLING and - * TIFFTAG_DOTRANGE tags in tif_dir.c. */ - char *tmp; raw_data = _TIFFmalloc( _TIFFDataSize(fip->field_type) * value_count); - tmp = raw_data; mem_alloc = 1; - if(TIFFGetField(tif, tag, tmp, - tmp + _TIFFDataSize(fip->field_type)) != 1) { + if(TIFFGetField(tif, tag, raw_data) != 1) { _TIFFfree(raw_data); continue; } @@ -574,7 +574,7 @@ * _TIFFPrettyPrintField() fall down and print it as any other * tag. */ - if (_TIFFPrettyPrintField(tif, fd, tag, value_count, raw_data)) { + if (_TIFFPrettyPrintField(tif, fip, fd, tag, value_count, raw_data)) { if(mem_alloc) _TIFFfree(raw_data); continue;