libtiff: Merge branch 'fix_579_non_terminated_ASCII_deletes_one_byte' into 'master'

From 5fd944398f2a582eeda89c45a10a6b66d6c48e8f Mon Sep 17 00:00:00 2001
From: Su_Laus <[EMAIL REDACTED]>
Date: Sun, 17 Dec 2023 22:12:33 +0100
Subject: [PATCH] For non-terminated ASCII arrays, the buffer is first enlarged
 before a NULL is set at the end to avoid deleting the last character. Fixes
 #579. Some editorials in tif_dir.c.

---
 libtiff/tif_dir.c     |  9 +++---
 libtiff/tif_dirread.c | 69 ++++++++++++++++++++++++++++++++-----------
 2 files changed, 57 insertions(+), 21 deletions(-)

diff --git a/libtiff/tif_dir.c b/libtiff/tif_dir.c
index 47ad21e0..2f7ed32a 100644
--- a/libtiff/tif_dir.c
+++ b/libtiff/tif_dir.c
@@ -211,7 +211,7 @@ static uint16_t countInkNamesString(TIFF *tif, uint32_t slen, const char *s)
     }
 bad:
     TIFFErrorExtR(tif, "TIFFSetField",
-                  "%s: Invalid InkNames value; no NUL at given buffer end "
+                  "%s: Invalid InkNames value; no null at given buffer end "
                   "location %" PRIu32 ", after %" PRIu16 " ink",
                   tif->tif_name, slen, i);
     return (0);
@@ -2156,8 +2156,9 @@ int TIFFSetSubDirectory(TIFF *tif, uint64_t diroff)
         if (retval)
         {
             /* Reset IFD list to start new one for SubIFD chain and also start
-            * SubIFD chain with tif_curdir=0. */
-            _TIFFCleanupIFDOffsetAndNumberMaps(tif); /* invalidate IFD loop lists */
+             * SubIFD chain with tif_curdir=0. */
+            /* invalidate IFD loop lists */
+            _TIFFCleanupIFDOffsetAndNumberMaps(tif);
             tif->tif_curdir = 0; /* first directory of new chain */
             /* add this offset to new IFD list */
             _TIFFCheckDirNumberAndOffset(tif, tif->tif_curdir, diroff);
@@ -2165,7 +2166,7 @@ int TIFFSetSubDirectory(TIFF *tif, uint64_t diroff)
         /* To be able to return from SubIFD or custom-IFD to main-IFD */
         tif->tif_setdirectory_force_absolute = TRUE;
     }
-    
+
     return (retval);
 }
 
diff --git a/libtiff/tif_dirread.c b/libtiff/tif_dirread.c
index 242912f3..0c70076f 100644
--- a/libtiff/tif_dirread.c
+++ b/libtiff/tif_dirread.c
@@ -6133,15 +6133,15 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                         fip->field_name);
                 else if (mb + 1 > (uint32_t)dp->tdir_count)
                 {
-                    uint8_t *o;
-                    TIFFWarningExtR(
-                        tif, module,
-                        "ASCII value for tag \"%s\" does not end in null byte",
-                        fip->field_name);
+                    TIFFWarningExtR(tif, module,
+                                    "ASCII value for tag \"%s\" does not end "
+                                    "in null byte. Forcing it to be null",
+                                    fip->field_name);
                     /* TIFFReadDirEntryArrayWithLimit() ensures this can't be
                      * larger than MAX_SIZE_TAG_DATA */
                     assert((uint32_t)dp->tdir_count + 1 == dp->tdir_count + 1);
-                    o = _TIFFmallocExt(tif, (uint32_t)dp->tdir_count + 1);
+                    uint8_t *o =
+                        _TIFFmallocExt(tif, (uint32_t)dp->tdir_count + 1);
                     if (o == NULL)
                     {
                         if (data != NULL)
@@ -6641,12 +6641,29 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                     if (data != 0 && dp->tdir_count > 0 &&
                         data[dp->tdir_count - 1] != '\0')
                     {
-                        TIFFWarningExtR(
-                            tif, module,
-                            "ASCII value for tag \"%s\" does not end in null "
-                            "byte. Forcing it to be null",
-                            fip->field_name);
-                        data[dp->tdir_count - 1] = '\0';
+                        TIFFWarningExtR(tif, module,
+                                        "ASCII value for ASCII array tag "
+                                        "\"%s\" does not end in null "
+                                        "byte. Forcing it to be null",
+                                        fip->field_name);
+                        /* Enlarge buffer and add terminating null. */
+                        uint8_t *o =
+                            _TIFFmallocExt(tif, (uint32_t)dp->tdir_count + 1);
+                        if (o == NULL)
+                        {
+                            if (data != NULL)
+                                _TIFFfreeExt(tif, data);
+                            return (0);
+                        }
+                        if (dp->tdir_count > 0)
+                        {
+                            _TIFFmemcpy(o, data, (uint32_t)dp->tdir_count);
+                        }
+                        o[(uint32_t)dp->tdir_count] = 0;
+                        dp->tdir_count++; /* Increment for added null. */
+                        if (data != 0)
+                            _TIFFfreeExt(tif, data);
+                        data = o;
                     }
                     m = TIFFSetField(tif, dp->tdir_tag,
                                      (uint16_t)(dp->tdir_count), data);
@@ -6923,11 +6940,29 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 if (data != 0 && dp->tdir_count > 0 &&
                     data[dp->tdir_count - 1] != '\0')
                 {
-                    TIFFWarningExtR(tif, module,
-                                    "ASCII value for tag \"%s\" does not end "
-                                    "in null byte. Forcing it to be null",
-                                    fip->field_name);
-                    data[dp->tdir_count - 1] = '\0';
+                    TIFFWarningExtR(
+                        tif, module,
+                        "ASCII value for ASCII array tag \"%s\" does not end "
+                        "in null byte. Forcing it to be null",
+                        fip->field_name);
+                    /* Enlarge buffer and add terminating null. */
+                    uint8_t *o =
+                        _TIFFmallocExt(tif, (uint32_t)dp->tdir_count + 1);
+                    if (o == NULL)
+                    {
+                        if (data != NULL)
+                            _TIFFfreeExt(tif, data);
+                        return (0);
+                    }
+                    if (dp->tdir_count > 0)
+                    {
+                        _TIFFmemcpy(o, data, (uint32_t)dp->tdir_count);
+                    }
+                    o[(uint32_t)dp->tdir_count] = 0;
+                    dp->tdir_count++; /* Increment for added null. */
+                    if (data != 0)
+                        _TIFFfreeExt(tif, data);
+                    data = o;
                 }
                 m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
                                  data);