libtiff: Add tests for IFD loop detection

From 565b8923affdc0851d85eafe63a048a58bf1a60b Mon Sep 17 00:00:00 2001
From: Even Rouault <[EMAIL REDACTED]>
Date: Tue, 13 Dec 2022 15:15:18 +0100
Subject: [PATCH] Add tests for IFD loop detection

---
 test/CMakeLists.txt                    |   6 +
 test/Makefile.am                       |   8 +-
 test/images/test_ifd_loop_to_first.tif | Bin 0 -> 312 bytes
 test/images/test_ifd_loop_to_self.tif  | Bin 0 -> 558 bytes
 test/images/test_two_ifds.tif          | Bin 0 -> 312 bytes
 test/test_ifd_loop_detection.c         | 179 +++++++++++++++++++++++++
 6 files changed, 192 insertions(+), 1 deletion(-)
 create mode 100644 test/images/test_ifd_loop_to_first.tif
 create mode 100644 test/images/test_ifd_loop_to_self.tif
 create mode 100644 test/images/test_two_ifds.tif
 create mode 100644 test/test_ifd_loop_detection.c

diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 742aba5b..907aed8f 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -185,6 +185,12 @@ target_sources(test_append_to_strip PRIVATE test_append_to_strip.c)
 target_link_libraries(test_append_to_strip PRIVATE tiff tiff_port)
 list(APPEND simple_tests test_append_to_strip)
 
+add_executable(test_ifd_loop_detection ../placeholder.h)
+target_sources(test_ifd_loop_detection PRIVATE test_ifd_loop_detection.c)
+target_link_libraries(test_ifd_loop_detection PRIVATE tiff tiff_port)
+target_compile_definitions(test_ifd_loop_detection PRIVATE SOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\")
+list(APPEND simple_tests test_ifd_loop_detection)
+
 if(WEBP_SUPPORT AND EMSCRIPTEN)
   # Emscripten is pretty finnicky about linker flags.
   # It needs --shared-memory if and only if atomics or bulk-memory is used.
diff --git a/test/Makefile.am b/test/Makefile.am
index f7c2477e..4ba5e07c 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -76,7 +76,7 @@ if TIFF_TESTS
 check_PROGRAMS = \
 	ascii_tag long_tag short_tag strip_rw rewrite custom_dir custom_dir_EXIF_231 \
 	defer_strile_loading defer_strile_writing test_directory test_open_options \
-	test_append_to_strip testtypes test_signed_tags $(JPEG_DEPENDENT_CHECK_PROG) $(STATIC_CHECK_PROGS)
+	test_append_to_strip test_ifd_loop_detection testtypes test_signed_tags $(JPEG_DEPENDENT_CHECK_PROG) $(STATIC_CHECK_PROGS)
 endif
 
 # Test scripts to execute
@@ -210,6 +210,9 @@ IMAGES_EXTRA_DIST = \
 	images/test_float64_predictor2_le_lzw.tif \
 	images/test_float64_predictor2_be_lzw.tif \
 	images/tiff_with_subifd_chain.tif \
+	images/test_ifd_loop_to_self.tif \
+	images/test_ifd_loop_to_first.tif \
+	images/test_two_ifds.tif \
 	$(PNMIMAGES) \
 	$(TIFFIMAGES)
 
@@ -250,6 +253,9 @@ test_open_options_SOURCES = test_open_options.c
 test_open_options_LDADD = $(LIBTIFF)
 test_append_to_strip_SOURCES = test_append_to_strip.c
 test_append_to_strip_LDADD = $(LIBTIFF)
+test_ifd_loop_detection_CFLAGS = -DSOURCE_DIR=\"@srcdir@\"
+test_ifd_loop_detection_SOURCES = test_ifd_loop_detection.c
+test_ifd_loop_detection_LDADD = $(LIBTIFF)
 
 AM_CPPFLAGS = -I$(top_srcdir)/libtiff
 
diff --git a/test/images/test_ifd_loop_to_first.tif b/test/images/test_ifd_loop_to_first.tif
new file mode 100644
index 0000000000000000000000000000000000000000..015c8d90d0d631c23792e8fc2c9e98af45f7809e
GIT binary patch
literal 312
zcmebD)MDUbU|`5*;9+23WM*IlvcVXM%>-q00OgpG#MzM8f{ZL+eIPYLNaCVMY%wIZ
bI8;5zE*T{8U?lb;pd7W_wSbE50)-a<l`sWq

literal 0
HcmV?d00001

diff --git a/test/images/test_ifd_loop_to_self.tif b/test/images/test_ifd_loop_to_self.tif
new file mode 100644
index 0000000000000000000000000000000000000000..26b585e960dbc259506326396796dbf238393436
GIT binary patch
literal 558
zcmYk4ty0B6424@R!yqs){1~2qH@R3`ZDeF*?2!?O2*lczD=V+W-q%6G$)%#Z(`hC-
zIr(<m<MD0!nx{z}1_S`*hPt&hzS6pdx(l2&X-17R69%WwOidH+Y<A;TOEnhJ%FN6>
zy*pIV{4_?UCHo9bs^zTTBO^d}qr8R;r=nCrFVJ3GRv{1Ug6Ixmt|muF2uFepEIQ9n
zt5`Kzh{2TqKS4xWbSPI%8!1&0Jq@LmgpSBMx6mCD1fYy;8Vl_g;_@s7Cg|CmHpby1
z1-ZDLMnpF(VheM0;N`TH1f=zM0!Kq9@c8*XL&FojC$Cbu*R^G9ZAIO9Efc+)rui`O
y!%UBS=3RWX#kb5KwzzaRyZC;-zVw#nVV8f}#m`$@{x6&UlSZ#Q&BrdTzvK_+G|CPD

literal 0
HcmV?d00001

diff --git a/test/images/test_two_ifds.tif b/test/images/test_two_ifds.tif
new file mode 100644
index 0000000000000000000000000000000000000000..4334649ea787c9b71f36eb918f715af88b296e2e
GIT binary patch
literal 312
zcmebD)MDUbU|`5*;9+23WM*IlvcVXM%>-q00OgpG#MzM8f{ZL+eIPYLNaCVMY%wIZ
cI8;5zE*T{8U?lb;pd7W_wSbE50vg2t0F?*@UjP6A

literal 0
HcmV?d00001

diff --git a/test/test_ifd_loop_detection.c b/test/test_ifd_loop_detection.c
new file mode 100644
index 00000000..52b089bf
--- /dev/null
+++ b/test/test_ifd_loop_detection.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2022, Even Rouault <even.rouault at spatialys.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library
+ *
+ * Test IFD loop detection
+ */
+
+#include "tif_config.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tiffio.h"
+
+int main()
+{
+    int ret = 0;
+    {
+        TIFF *tif =
+            TIFFOpen(SOURCE_DIR "/images/test_ifd_loop_to_self.tif", "r");
+        assert(tif);
+        if (TIFFReadDirectory(tif))
+        {
+            fprintf(stderr, "(1) Expected TIFFReadDirectory() to fail\n");
+            ret = 1;
+        }
+        TIFFClose(tif);
+    }
+    {
+        TIFF *tif =
+            TIFFOpen(SOURCE_DIR "/images/test_ifd_loop_to_self.tif", "r");
+        assert(tif);
+        int n = TIFFNumberOfDirectories(tif);
+        if (n != 1)
+        {
+            fprintf(
+                stderr,
+                "(2) Expected TIFFNumberOfDirectories() to return 1. Got %d\n",
+                n);
+            ret = 1;
+        }
+        TIFFClose(tif);
+    }
+    {
+        TIFF *tif =
+            TIFFOpen(SOURCE_DIR "/images/test_ifd_loop_to_first.tif", "r");
+        assert(tif);
+        if (TIFFReadDirectory(tif) != 1)
+        {
+            fprintf(stderr, "(3) Expected TIFFReadDirectory() to succeed\n");
+            ret = 1;
+        }
+        if (TIFFReadDirectory(tif))
+        {
+            fprintf(stderr, "(4) Expected TIFFReadDirectory() to fail\n");
+            ret = 1;
+        }
+        if (TIFFSetDirectory(tif, 1) != 1)
+        {
+            fprintf(stderr, "(5) Expected TIFFSetDirectory() to succeed\n");
+            ret = 1;
+        }
+        if (TIFFReadDirectory(tif))
+        {
+            fprintf(stderr, "(6) Expected TIFFReadDirectory() to fail\n");
+            ret = 1;
+        }
+        if (TIFFSetDirectory(tif, 0) != 1)
+        {
+            fprintf(stderr, "(7) Expected TIFFSetDirectory() to succeed\n");
+            ret = 1;
+        }
+        if (TIFFReadDirectory(tif) != 1)
+        {
+            fprintf(stderr, "(8) Expected TIFFReadDirectory() to succeed\n");
+            ret = 1;
+        }
+        if (TIFFReadDirectory(tif))
+        {
+            fprintf(stderr, "(9) Expected TIFFReadDirectory() to fail\n");
+            ret = 1;
+        }
+        TIFFClose(tif);
+    }
+    {
+        TIFF *tif =
+            TIFFOpen(SOURCE_DIR "/images/test_ifd_loop_to_first.tif", "r");
+        assert(tif);
+        int n = TIFFNumberOfDirectories(tif);
+        if (n != 2)
+        {
+            fprintf(
+                stderr,
+                "(10) Expected TIFFNumberOfDirectories() to return 2. Got %d\n",
+                n);
+            ret = 1;
+        }
+        TIFFClose(tif);
+    }
+    {
+        TIFF *tif = TIFFOpen(SOURCE_DIR "/images/test_two_ifds.tif", "r");
+        assert(tif);
+        if (TIFFReadDirectory(tif) != 1)
+        {
+            fprintf(stderr, "(11) Expected TIFFReadDirectory() to succeed\n");
+            ret = 1;
+        }
+        if (TIFFReadDirectory(tif))
+        {
+            fprintf(stderr, "(12) Expected TIFFReadDirectory() to fail\n");
+            ret = 1;
+        }
+        if (TIFFSetDirectory(tif, 1) != 1)
+        {
+            fprintf(stderr, "(13) Expected TIFFSetDirectory() to succeed\n");
+            ret = 1;
+        }
+        if (TIFFReadDirectory(tif))
+        {
+            fprintf(stderr, "(14) Expected TIFFReadDirectory() to fail\n");
+            ret = 1;
+        }
+        if (TIFFSetDirectory(tif, 0) != 1)
+        {
+            fprintf(stderr, "(15) Expected TIFFSetDirectory() to succeed\n");
+            ret = 1;
+        }
+        if (TIFFReadDirectory(tif) != 1)
+        {
+            fprintf(stderr, "(16) Expected TIFFReadDirectory() to succeed\n");
+            ret = 1;
+        }
+        if (TIFFReadDirectory(tif))
+        {
+            fprintf(stderr, "(17) Expected TIFFReadDirectory() to fail\n");
+            ret = 1;
+        }
+        TIFFClose(tif);
+    }
+    {
+        TIFF *tif = TIFFOpen(SOURCE_DIR "/images/test_two_ifds.tif", "r");
+        assert(tif);
+        int n = TIFFNumberOfDirectories(tif);
+        if (n != 2)
+        {
+            fprintf(
+                stderr,
+                "(18) Expected TIFFNumberOfDirectories() to return 2. Got %d\n",
+                n);
+            ret = 1;
+        }
+        TIFFClose(tif);
+    }
+    return ret;
+}