libtiff: Add TIFFOpenOptionsSetMaxSingleMemAlloc()

From b88c3f9af25955a0df90a2f4fc2ac75fd4cce209 Mon Sep 17 00:00:00 2001
From: Even Rouault <[EMAIL REDACTED]>
Date: Tue, 22 Nov 2022 18:17:34 +0100
Subject: [PATCH] Add TIFFOpenOptionsSetMaxSingleMemAlloc()

to define a limit in bytes for a single memory allocation done by libtiff.

Also add internal functions used in replacement of the non Ext ones:
void* _TIFFmallocExt(TIFF* tif, tmsize_t s);
void* _TIFFcallocExt(TIFF* tif, tmsize_t nmemb, tmsize_t siz);
void* _TIFFreallocExt(TIFF* tif, void* p, tmsize_t s);
void _TIFFfreeExt(TIFF* tif, void* p);
---
 libtiff/libtiff.def |  1 +
 libtiff/libtiff.map |  1 +
 libtiff/tif_open.c  | 57 +++++++++++++++++++++++++++++++++++++++++++--
 libtiff/tiffio.h    |  1 +
 libtiff/tiffiop.h   |  7 ++++++
 5 files changed, 65 insertions(+), 2 deletions(-)

diff --git a/libtiff/libtiff.def b/libtiff/libtiff.def
index 975ad1fd..22cbb1aa 100644
--- a/libtiff/libtiff.def
+++ b/libtiff/libtiff.def
@@ -85,6 +85,7 @@ EXPORTS	TIFFAccessTagMethods
 	TIFFOpenWExt
 	TIFFOpenOptionsAlloc
 	TIFFOpenOptionsFree
+	TIFFOpenOptionsSetMaxSingleMemAlloc
 	TIFFOpenOptionsSetErrorHandlerExtR
 	TIFFOpenOptionsSetWarningHandlerExtR
 	TIFFPrintDirectory
diff --git a/libtiff/libtiff.map b/libtiff/libtiff.map
index c811a751..05da6d7e 100644
--- a/libtiff/libtiff.map
+++ b/libtiff/libtiff.map
@@ -210,6 +210,7 @@ LIBTIFF_4.5 {
     TIFFWarningExtR;
     TIFFOpenOptionsAlloc;
     TIFFOpenOptionsFree;
+    TIFFOpenOptionsSetMaxSingleMemAlloc;
     TIFFOpenOptionsSetErrorHandlerExtR;
     TIFFOpenOptionsSetWarningHandlerExtR;
 } LIBTIFF_4.4;
diff --git a/libtiff/tif_open.c b/libtiff/tif_open.c
index 23e206d7..76158096 100644
--- a/libtiff/tif_open.c
+++ b/libtiff/tif_open.c
@@ -78,6 +78,15 @@ void TIFFOpenOptionsFree(TIFFOpenOptions* opts)
     _TIFFfree(opts);
 }
 
+/** Define a limit in bytes for a single memory allocation done by libtiff.
+ *  If max_single_mem_alloc is set to 0, no other limit that the underlying
+ *  _TIFFmalloc() will be applied, which is the default.
+ */
+void TIFFOpenOptionsSetMaxSingleMemAlloc(TIFFOpenOptions* opts, tmsize_t max_single_mem_alloc)
+{
+    opts->max_single_mem_alloc = max_single_mem_alloc;
+}
+
 void TIFFOpenOptionsSetErrorHandlerExtR(TIFFOpenOptions* opts, TIFFErrorHandlerExtR handler, void* errorhandler_user_data)
 {
     opts->errorhandler = handler;
@@ -90,6 +99,43 @@ void TIFFOpenOptionsSetWarningHandlerExtR(TIFFOpenOptions* opts, TIFFErrorHandle
     opts->warnhandler_user_data = warnhandler_user_data;
 }
 
+/** malloc() version that takes into account memory-specific open options */
+void* _TIFFmallocExt(TIFF* tif, tmsize_t s)
+{
+    if (tif != NULL && tif->tif_max_single_mem_alloc > 0 && s > tif->tif_max_single_mem_alloc)
+        return NULL;
+    return _TIFFmalloc(s);
+}
+
+/** calloc() version that takes into account memory-specific open options */
+void* _TIFFcallocExt(TIFF* tif, tmsize_t nmemb, tmsize_t siz)
+{
+    if( tif != NULL )
+    {
+        if (nmemb <= 0 || siz <= 0 || nmemb > TIFF_TMSIZE_T_MAX / siz)
+            return NULL;
+        if (tif->tif_max_single_mem_alloc > 0 && nmemb * siz > tif->tif_max_single_mem_alloc)
+            return NULL;
+    }
+    return _TIFFcalloc(nmemb, siz);
+}
+
+/** realloc() version that takes into account memory-specific open options */
+void* _TIFFreallocExt(TIFF* tif, void* p, tmsize_t s)
+{
+    if (tif != NULL && tif->tif_max_single_mem_alloc > 0 && s > tif->tif_max_single_mem_alloc)
+        return NULL;
+    return _TIFFrealloc(p, s);
+}
+
+/** free() version that takes into account memory-specific open options */
+void _TIFFfreeExt(TIFF* tif, void* p)
+{
+    (void)tif;
+    _TIFFfree(p);
+}
+
+
 TIFF*
 TIFFClientOpen(
 	const char* name, const char* mode,
@@ -160,7 +206,13 @@ TIFFClientOpenExt(
 	m = _TIFFgetMode(opts, clientdata, mode, module);
 	if (m == -1)
 		goto bad2;
-	tif = (TIFF *)_TIFFmalloc((tmsize_t)(sizeof (TIFF) + strlen(name) + 1));
+	tmsize_t size_to_alloc = (tmsize_t)(sizeof (TIFF) + strlen(name) + 1);
+	if (opts && opts->max_single_mem_alloc > 0 &&
+	    size_to_alloc > opts->max_single_mem_alloc) {
+		_TIFFErrorEarly(opts, clientdata, module, "%s: Out of memory (TIFF structure)", name);
+		goto bad2;
+	}
+	tif = (TIFF *)_TIFFmallocExt(NULL, size_to_alloc);
 	if (tif == NULL) {
 		_TIFFErrorEarly(opts, clientdata, module, "%s: Out of memory (TIFF structure)", name);
 		goto bad2;
@@ -187,12 +239,13 @@ TIFFClientOpenExt(
         tif->tif_errorhandler_user_data = opts->errorhandler_user_data;
         tif->tif_warnhandler = opts->warnhandler;
         tif->tif_warnhandler_user_data = opts->warnhandler_user_data;
+        tif->tif_max_single_mem_alloc = opts->max_single_mem_alloc;
     }
 
 	if (!readproc || !writeproc || !seekproc || !closeproc || !sizeproc) {
 		TIFFErrorExtR(tif, module,
 		    "One of the client procedures is NULL pointer.");
-		_TIFFfree(tif);
+		_TIFFfreeExt(NULL, tif);
 		goto bad2;
 	}
 
diff --git a/libtiff/tiffio.h b/libtiff/tiffio.h
index 7d12a88c..54a646d8 100644
--- a/libtiff/tiffio.h
+++ b/libtiff/tiffio.h
@@ -466,6 +466,7 @@ extern void TIFFErrorExtR(TIFF*, const char*, const char*, ...) TIFF_ATTRIBUTE((
 typedef struct TIFFOpenOptions TIFFOpenOptions;
 extern TIFFOpenOptions* TIFFOpenOptionsAlloc(void);
 extern void TIFFOpenOptionsFree(TIFFOpenOptions*);
+extern void TIFFOpenOptionsSetMaxSingleMemAlloc(TIFFOpenOptions* opts, tmsize_t max_single_mem_alloc);
 extern void TIFFOpenOptionsSetErrorHandlerExtR(TIFFOpenOptions* opts, TIFFErrorHandlerExtR handler, void* errorhandler_user_data);
 extern void TIFFOpenOptionsSetWarningHandlerExtR(TIFFOpenOptions* opts, TIFFErrorHandlerExtR handler, void* warnhandler_user_data);
 
diff --git a/libtiff/tiffiop.h b/libtiff/tiffiop.h
index d52fc45f..2399d1e1 100644
--- a/libtiff/tiffiop.h
+++ b/libtiff/tiffiop.h
@@ -202,6 +202,7 @@ struct tiff {
     void*                 tif_errorhandler_user_data;
     TIFFErrorHandlerExtR  tif_warnhandler;
     void*                 tif_warnhandler_user_data;
+    tmsize_t              tif_max_single_mem_alloc; /* in bytes. 0 for unlimited */
 };
 
 struct TIFFOpenOptions
@@ -210,6 +211,7 @@ struct TIFFOpenOptions
     void*                errorhandler_user_data; /* may be NULL */
     TIFFErrorHandlerExtR warnhandler; /* may be NULL */
     void*                warnhandler_user_data; /* may be NULL */
+    tmsize_t             max_single_mem_alloc; /* in bytes. 0 for unlimited */
 };
 
 #define isPseudoTag(t) (t > 0xffff)            /* is tag value normal or pseudo */
@@ -448,6 +450,11 @@ extern const TIFFCodec _TIFFBuiltinCODECS[];
 extern void TIFFCIELab16ToXYZ(TIFFCIELabToRGB *, uint32_t l, int32_t a, int32_t b,
                               float *, float *, float *);
 
+extern void* _TIFFmallocExt(TIFF* tif, tmsize_t s);
+extern void* _TIFFcallocExt(TIFF* tif, tmsize_t nmemb, tmsize_t siz);
+extern void* _TIFFreallocExt(TIFF* tif, void* p, tmsize_t s);
+extern void _TIFFfreeExt(TIFF* tif, void* p);
+
 #if defined(__cplusplus)
 }
 #endif