libtiff: TIFFFetchNormalTag(): speed optimization when reading a (very large) nul-terminated ASCII tag

https://github.com/libsdl-org/libtiff/commit/6fc45de7596acdf7750f8baab7b208d97cfd3267

From 6fc45de7596acdf7750f8baab7b208d97cfd3267 Mon Sep 17 00:00:00 2001
From: Even Rouault <[EMAIL REDACTED]>
Date: Sat, 19 Feb 2022 16:10:31 +0100
Subject: [PATCH] TIFFFetchNormalTag(): speed optimization when reading a (very
 large) nul-terminated ASCII tag

---
 libtiff/tif_dirread.c | 44 ++++++++++++++++++++++++++++---------------
 1 file changed, 29 insertions(+), 15 deletions(-)

diff --git a/libtiff/tif_dirread.c b/libtiff/tif_dirread.c
index 2ec44a4f..3ed6da1d 100644
--- a/libtiff/tif_dirread.c
+++ b/libtiff/tif_dirread.c
@@ -38,6 +38,7 @@
 #include "tiffiop.h"
 #include <float.h>
 #include <stdlib.h>
+#include <string.h>
 
 #define FAILED_FII    ((uint32_t) -1)
 
@@ -826,6 +827,10 @@ static enum TIFFReadDirEntryErr TIFFReadDirEntryDataAndRealloc(
         return TIFFReadDirEntryErrOk;
 }
 
+/* Caution: if raising that value, make sure int32 / uint32 overflows can't occur
+ * elsewhere */
+#define MAX_SIZE_TAG_DATA       2147483647U
+
 static enum TIFFReadDirEntryErr TIFFReadDirEntryArrayWithLimit(
         TIFF* tif, TIFFDirEntry* direntry, uint32_t* count, uint32_t desttypesize,
         void** value, uint64_t maxcount)
@@ -858,9 +863,9 @@ static enum TIFFReadDirEntryErr TIFFReadDirEntryArrayWithLimit(
          * in either the current data type or the dest data type.  This also
          * avoids problems with overflow of tmsize_t on 32bit systems.
          */
-	if ((uint64_t)(2147483647 / typesize) < target_count64)
+	if ((uint64_t)(MAX_SIZE_TAG_DATA / typesize) < target_count64)
 		return(TIFFReadDirEntryErrSizesan);
-	if ((uint64_t)(2147483647 / desttypesize) < target_count64)
+	if ((uint64_t)(MAX_SIZE_TAG_DATA / desttypesize) < target_count64)
 		return(TIFFReadDirEntryErrSizesan);
 
 	*count=(uint32_t)target_count64;
@@ -5062,18 +5067,28 @@ TIFFFetchNormalTag(TIFF* tif, TIFFDirEntry* dp, int recover)
 				err=TIFFReadDirEntryByteArray(tif,dp,&data);
 				if (err==TIFFReadDirEntryErrOk)
 				{
-					uint32_t mb = 0;
+					size_t mb = 0;
 					int n;
 					if (data != NULL)
 					{
-					    uint8_t* ma = data;
-					    while (mb<(uint32_t)dp->tdir_count)
-					    {
-					            if (*ma==0)
-					                    break;
-					            ma++;
-					            mb++;
-					    }
+						if (dp->tdir_count > 0 && data[dp->tdir_count - 1] == 0)
+						{
+							/* optimization: if data is known to be 0 terminated, we can use strlen() */
+							mb = strlen((const char*)data);
+						}
+						else
+						{
+							/* general case. equivalent to non-portable */
+							/* mb = strnlen((const char*)data, (uint32_t)dp->tdir_count); */
+							uint8_t* ma = data;
+							while (mb<(uint32_t)dp->tdir_count)
+							{
+								if (*ma==0)
+									break;
+								ma++;
+								mb++;
+							}
+						}
 					}
 					if (mb+1<(uint32_t)dp->tdir_count)
 						TIFFWarningExt(tif->tif_clientdata,module,"ASCII value for tag \"%s\" contains null byte in value; value incorrectly truncated during reading due to implementation limitations",fip->field_name);
@@ -5081,10 +5096,9 @@ TIFFFetchNormalTag(TIFF* tif, TIFFDirEntry* dp, int recover)
 					{
 						uint8_t* o;
 						TIFFWarningExt(tif->tif_clientdata,module,"ASCII value for tag \"%s\" does not end in null byte",fip->field_name);
-						if ((uint32_t)dp->tdir_count + 1 != dp->tdir_count + 1)
-							o=NULL;
-						else
-							o=_TIFFmalloc((uint32_t)dp->tdir_count + 1);
+						/* TIFFReadDirEntryArrayWithLimit() ensures this can't be larger than MAX_SIZE_TAG_DATA */
+						assert((uint32_t)dp->tdir_count + 1 == dp->tdir_count + 1);
+						o=_TIFFmalloc((uint32_t)dp->tdir_count + 1);
 						if (o==NULL)
 						{
 							if (data!=NULL)