2009.05.19 12:32 "[Tiff] Heap corruption caused by TIFFRGBAImageGet() + TIFFSetDirectory() + TIFFRewriteDirectory()", by

2009.05.19 12:32 "[Tiff] Heap corruption caused by TIFFRGBAImageGet() + TIFFSetDirectory() + TIFFRewriteDirectory()", by

TIFFStartStrip() uses the raw buffer for *reading* data and sets tif_rawcc to a positive value:

     tif->tif_rawcc = td->td_stripbytecount[strip];

TIFFReadDirectory() resets tif_curstrip to -1, but doesn't reset tif_rawcc to 0:

     tif->tif_curstrip = (tstrip_t) -1;

In consequence, the next call to _TIFFWriteDirectory() thinks there is pending data to *write*:

         if (tif->tif_rawcc > 0 && !TIFFFlushData1(tif)) {

and TIFFAppendToStrip() will modify td_stripoffset[-1] (as strip = tif->tif_curstrip = (tstrip_t)-1):

                                     td->td_stripoffset[strip] =
                                             TIFFSeekFile(tif, (toff_t)0,
                                                          SEEK_END);

Probably ditto for tiled images. There seems to be no way to reset tif_rawcc to 0 with the official

API except for reopening the image.

When built with dmalloc, the program below will result in this output:

libtiff version: LIBTIFF, Version 3.8.2
Copyright (c) 1988-1996 Sam Leffler
Copyright (c) 1991-1996 Silicon Graphics, Inc.
tif_rawcc=115
tif_curtile=-1
tif_curstrip=2338

tif_rawcc=115
tif_curtile=-1
tif_curstrip=-1

debug-malloc library: dumping program, fatal error
   Error: failed UNDER picket-fence magic-number check (err 26)
Aborted

Here's the program:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "tiffio.h"
#ifdef HAVE_TIFFIOP_H
#include "tiffiop.h"
#endif

static void show (TIFF *tif)
{
#ifdef HAVE_TIFFIOP_H
   printf ("tif_rawcc=%ld\n", (long)tif->tif_rawcc);
   printf ("tif_curtile=%ld\n", (long)tif->tif_curtile);
   printf ("tif_curstrip=%ld\n", (long)tif->tif_curstrip);
   printf ("\n");
#endif
}

int main (int argc, char *argv[])
{
   const char *path;
   TIFF *tif;
   uint32 width, height;
   TIFFRGBAImage img;
   size_t size;
   uint32 *raster;
   char msg[1024];

printf ("libtiff version: %s\n", TIFFGetVersion ());
if (argc != 2)
  {
    fprintf (stderr, "Usage: %s TIFF_FILE\n", argv[0]);
    exit (1);
  }
path = argv[1];
tif = TIFFOpen (path, "r+");
if (tif == NULL)
  {
    perror (path);
    exit (2);
  }

if (TIFFRGBAImageOK (tif, msg) != 1)
  {
    fprintf (stderr, "Cannot process image as RGBA (%s)\n", msg);
    exit (2);
  }
if (TIFFRGBAImageBegin (&img, tif, 1, msg) != 1)
  {
    fprintf (stderr, "TIFFRGBAImageBegin failed (%s)\n", msg);
    exit (2);
  }
TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &height);
size = (size_t)-1;
if (width > size / height / sizeof (uint32))
  {
    fprintf (stderr, "Image too big\n");
    exit (2);
  }
size = width * height * sizeof (uint32);
raster = (uint32 *)malloc (size);
if (raster == NULL)
  {
    fprintf (stderr, "Out of memory\n");
    exit (2);
  }
if (TIFFRGBAImageGet (&img, raster, width, height) != 1)
  {
    fprintf (stderr, "TIFFRGBAImageGet failed\n");
    exit (2);
  }
TIFFRGBAImageEnd (&img);

show (tif);

if (TIFFSetDirectory (tif, 0) != 1)
  {
    fprintf (stderr, "TIFFSetDirectory failed\n");
    exit (2);
  }

show (tif);

/*... set some fields here... */

if (TIFFRewriteDirectory (tif) != 1)
  {
    fprintf (stderr, "TIFFRewriteDirectory failed\n");
    exit (2);
  }

   TIFFClose (tif);
   return 0;
}