avoid looping badly on IFD loops in TIFF images, assuming a recent enough libtiff
authorTony Cook <tony@develop-help.com>
Mon, 15 Aug 2011 08:46:17 +0000 (18:46 +1000)
committerTony Cook <tony@develop-help.com>
Mon, 15 Aug 2011 08:46:17 +0000 (18:46 +1000)
Switch to TIFFReadDirectory() instead of TIFFSetDirectory(), since
TIFFReadDirectory() does IFD loop detection.

Unfortunately it doesn't return an indication that there *is* a loop,
it just indicates the end of the chain, so we can't warn the caller
about the problem.

TIFF/imtiff.c
TIFF/t/t10tiff.t

index bced21b..59c5173 100644 (file)
@@ -531,6 +531,7 @@ i_readtiff_wiol(io_glue *ig, int allow_incomplete, int page) {
   TIFFErrorHandler old_handler;
   TIFFErrorHandler old_warn_handler;
   i_img *im;
   TIFFErrorHandler old_handler;
   TIFFErrorHandler old_warn_handler;
   i_img *im;
+  int current_page;
 
   i_clear_error();
   old_handler = TIFFSetErrorHandler(error_handler);
 
   i_clear_error();
   old_handler = TIFFSetErrorHandler(error_handler);
@@ -562,8 +563,8 @@ i_readtiff_wiol(io_glue *ig, int allow_incomplete, int page) {
     return NULL;
   }
 
     return NULL;
   }
 
-  if (page != 0) {
-    if (!TIFFSetDirectory(tif, page)) {
+  for (current_page = 0; current_page < page; ++current_page) {
+    if (!TIFFReadDirectory(tif)) {
       mm_log((1, "i_readtiff_wiol: Unable to switch to directory %d\n", page));
       i_push_errorf(0, "could not switch to page %d", page);
       TIFFSetErrorHandler(old_handler);
       mm_log((1, "i_readtiff_wiol: Unable to switch to directory %d\n", page));
       i_push_errorf(0, "could not switch to page %d", page);
       TIFFSetErrorHandler(old_handler);
@@ -650,7 +651,7 @@ i_readtiff_multi_wiol(io_glue *ig, int *count) {
       }
     }
     results[*count-1] = im;
       }
     }
     results[*count-1] = im;
-  } while (TIFFSetDirectory(tif, ++dirnum));
+  } while (TIFFReadDirectory(tif));
 
   TIFFSetWarningHandler(old_warn_handler);
   TIFFSetErrorHandler(old_handler);
 
   TIFFSetWarningHandler(old_warn_handler);
   TIFFSetErrorHandler(old_handler);
index 8b15b84..403b59d 100644 (file)
@@ -768,3 +768,40 @@ SKIP:
      "should succeed - just inside bytes limit");
   Imager->set_file_limits(reset=>1);
 }
      "should succeed - just inside bytes limit");
   Imager->set_file_limits(reset=>1);
 }
+
+{
+  # this image has an IFD loop, which sends some TIFF readers into a
+  # loop, including Corel PhotoPaint and the GIMP's tiff reader.
+  my $ifdloop_hex = <<HEX;
+49 49 2A 00 0A 00 00 00 FE 00 0A 00 00 01 03 00
+01 00 00 00 01 00 00 00 01 01 03 00 01 00 00 00
+01 00 00 00 02 01 03 00 03 00 00 00 88 00 00 00
+03 01 03 00 01 00 00 00 05 80 00 00 06 01 03 00
+01 00 00 00 02 00 00 00 11 01 04 00 01 00 00 00
+08 00 00 00 12 01 03 00 01 00 00 00 01 00 00 00
+15 01 03 00 01 00 00 00 03 00 00 00 17 01 04 00
+01 00 00 00 02 00 00 00 1C 01 03 00 01 00 00 00
+01 00 00 00 90 00 00 00 08 00 08 00 08 00 FE 00
+0A 00 00 01 03 00 01 00 00 00 01 00 00 00 01 01
+03 00 01 00 00 00 01 00 00 00 02 01 03 00 03 00
+00 00 0E 01 00 00 03 01 03 00 01 00 00 00 05 80
+00 00 06 01 03 00 01 00 00 00 02 00 00 00 11 01
+04 00 01 00 00 00 8E 00 00 00 12 01 03 00 01 00
+00 00 01 00 00 00 15 01 03 00 01 00 00 00 03 00
+00 00 17 01 04 00 01 00 00 00 02 00 00 00 1C 01
+03 00 01 00 00 00 01 00 00 00 0A 00 00 00 08 00
+08 00 08 00
+HEX
+  $ifdloop_hex =~ tr/0-9A-F//cd;
+  my $ifdloop = pack("H*", $ifdloop_hex);
+
+  my $im = Imager->new;
+  ok($im->read(data => $ifdloop, type => "tiff", page => 1),
+     "read what should be valid");
+  ok(!$im->read(data => $ifdloop, type => "tiff", page => 2),
+     "third page is after looping back to the start, if this fails, upgrade tifflib")
+    or skip("tifflib is broken", 1);
+  print "# ", $im->errstr, "\n";
+  my @im = Imager->read_multi(type => "tiff", data => $ifdloop);
+  is(@im, 2, "should be only 2 images");
+}