libtiff: TIFFSetValue(): Writing IFD8 & LONG8 tags to ClassicTIFF corrected (fixes #442)

From af1cf0789aef7baa4c8093b52058bbcc4672c1f7 Mon Sep 17 00:00:00 2001
From: Su Laus <[EMAIL REDACTED]>
Date: Tue, 9 Aug 2022 15:15:57 +0000
Subject: [PATCH] TIFFSetValue(): Writing IFD8 & LONG8 tags to ClassicTIFF
 corrected (fixes #442)

---
 libtiff/tif_dir.c      | 70 +++++++++++++++++++++++++++++-
 libtiff/tif_dirwrite.c | 97 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 164 insertions(+), 3 deletions(-)

diff --git a/libtiff/tif_dir.c b/libtiff/tif_dir.c
index e90f14a0..ce3e2297 100644
--- a/libtiff/tif_dir.c
+++ b/libtiff/tif_dir.c
@@ -652,6 +652,30 @@ _TIFFVSetField(TIFF* tif, uint32_t tag, va_list ap)
 			  /*--: Rational2Double: For Rationals tv_size is set above to 4 or 8 according to fip->set_field_type! */
 				_TIFFmemcpy(tv->value, va_arg(ap, void *),
 				    tv->count * tv_size);
+				/* Test here for too big values for LONG8, SLONG8 in ClassicTIFF and delete custom field from custom list */
+				if (!(tif->tif_flags & TIFF_BIGTIFF)) {
+					if (tv->info->field_type == TIFF_LONG8) {
+						uint64_t *pui64 = (uint64_t *)tv->value;
+						for (int i = 0; i < tv->count; i++) {
+							if (pui64[i] > 0xffffffffu) {
+								TIFFErrorExt(tif->tif_clientdata, module,
+									"%s: Bad LONG8 value %"PRIu64" at %d. array position for \"%s\" tag %d in ClassicTIFF. Tag won't be written to file",
+									tif->tif_name, pui64[i], i, fip ? fip->field_name : "Unknown", tag);
+								goto badvalueifd8long8;
+							}
+						}
+					} else if (tv->info->field_type == TIFF_SLONG8) {
+						int64_t *pi64 = (int64_t *)tv->value;
+						for (int i = 0; i < tv->count; i++) {
+							if (pi64[i] > 2147483647 || pi64[i] < (-2147483647 - 1)) {
+								TIFFErrorExt(tif->tif_clientdata, module,
+									"%s: Bad SLONG8 value %"PRIi64" at %d. array position for \"%s\" tag %d in ClassicTIFF. Tag won't be written to file",
+									tif->tif_name, pi64[i], i, fip ? fip->field_name : "Unknown", tag);
+								goto badvalueifd8long8;
+							}
+						}
+					}
+				}
 			} else {
 				char *val = (char *)tv->value;
 				assert( tv->count == 1 );
@@ -700,12 +724,26 @@ _TIFFVSetField(TIFF* tif, uint32_t tag, va_list ap)
 					{
 						uint64_t v2 = va_arg(ap, uint64_t);
 						_TIFFmemcpy(val, &v2, tv_size);
+						/* Test here for too big values for ClassicTIFF and delete custom field from custom list */
+						if (!(tif->tif_flags & TIFF_BIGTIFF) && (v2 > 0xffffffffu)) {
+							TIFFErrorExt(tif->tif_clientdata, module,
+								"%s: Bad LONG8 or IFD8 value %"PRIu64" for \"%s\" tag %d in ClassicTIFF. Tag won't be written to file",
+								tif->tif_name, v2, fip ? fip->field_name : "Unknown", tag);
+							goto badvalueifd8long8;
+						}
 					}
 					break;
 				case TIFF_SLONG8:
 					{
 						int64_t v2 = va_arg(ap, int64_t);
 						_TIFFmemcpy(val, &v2, tv_size);
+						/* Test here for too big values for ClassicTIFF and delete custom field from custom list */
+						if (!(tif->tif_flags & TIFF_BIGTIFF) && ((v2 > 2147483647) || (v2 < (-2147483647 - 1)))) {
+							TIFFErrorExt(tif->tif_clientdata, module,
+								"%s: Bad SLONG8 value %"PRIi64" for \"%s\" tag %d in ClassicTIFF. Tag won't be written to file",
+								tif->tif_name, v2, fip ? fip->field_name : "Unknown", tag);
+								goto badvalueifd8long8;
+						}
 					}
 					break;
 				case TIFF_RATIONAL:
@@ -787,7 +825,37 @@ _TIFFVSetField(TIFF* tif, uint32_t tag, va_list ap)
         va_end(ap);
         }
     return (0);
-}
+badvalueifd8long8:
+		{
+			/* Error message issued already above. */
+			TIFFTagValue *tv2 = NULL;
+			int iCustom2, iC2;
+			/* Find the existing entry for this custom value. */
+			for (iCustom2 = 0; iCustom2 < td->td_customValueCount; iCustom2++) {
+				if (td->td_customValues[iCustom2].info->field_tag == tag) {
+					tv2 = td->td_customValues + (iCustom2);
+					break;
+				}
+			}
+			if (tv2 != NULL) {
+				/* Remove custom field from custom list */
+				if (tv2->value != NULL) {
+					_TIFFfree(tv2->value);
+					tv2->value = NULL;
+				}
+				/* Shorten list and close gap in customValues list.
+				 * Re-allocation of td_customValues not necessary here. */
+				td->td_customValueCount--;
+				for (iC2 = iCustom2; iC2 < td->td_customValueCount; iC2++) {
+					td->td_customValues[iC2] = td->td_customValues[iC2+1];
+				}
+			} else {
+				assert(0);
+			}
+			va_end(ap);
+		}
+	return (0);
+} /*-- _TIFFVSetField() --*/
 
 /*
  * Return 1/0 according to whether or not
diff --git a/libtiff/tif_dirwrite.c b/libtiff/tif_dirwrite.c
index 2fef6d82..1d8b80a8 100644
--- a/libtiff/tif_dirwrite.c
+++ b/libtiff/tif_dirwrite.c
@@ -1553,15 +1553,59 @@ TIFFWriteDirectoryTagLong8(TIFF* tif, uint32_t* ndir, TIFFDirEntry* dir, uint16_
 }
 #endif
 
+/************************************************************************/
+/*                 TIFFWriteDirectoryTagLong8Array()                    */
+/*                                                                      */
+/*      Write either Long8 or Long array depending on file type.        */
+/************************************************************************/
 static int
 TIFFWriteDirectoryTagLong8Array(TIFF* tif, uint32_t* ndir, TIFFDirEntry* dir, uint16_t tag, uint32_t count, uint64_t* value)
 {
+	static const char module[] = "TIFFWriteDirectoryTagLong8Array";
+	uint64_t *ma;
+	uint32_t mb;
+	uint32_t *p;
+	uint32_t *q;
+	int o;
+
+	/* is this just a counting pass? */
 	if (dir==NULL)
 	{
 		(*ndir)++;
 		return(1);
 	}
-	return(TIFFWriteDirectoryTagCheckedLong8Array(tif,ndir,dir,tag,count,value));
+
+	/* We always write Long8 for BigTIFF, no checking needed. */
+	if (tif->tif_flags & TIFF_BIGTIFF)
+		return(TIFFWriteDirectoryTagCheckedLong8Array(tif, ndir, dir, tag, count, value));
+
+	/*
+	** For classic tiff we want to verify everything is in range for long
+	** and convert to long format.
+	*/
+	p = _TIFFmalloc(count * sizeof(uint32_t));
+	if (p == NULL)
+	{
+		TIFFErrorExt(tif->tif_clientdata, module, "Out of memory");
+		return(0);
+	}
+
+	for (q = p, ma = value, mb = 0; mb < count; ma++, mb++, q++)
+	{
+		if (*ma > 0xFFFFFFFF)
+		{
+			TIFFErrorExt(tif->tif_clientdata, module,
+				"Attempt to write unsigned long value %"PRIu64" larger than 0xFFFFFFFF for tag %d in Classic TIFF file. TIFF file writing aborted", *ma, tag);
+			_TIFFfree(p);
+			return(0);
+		}
+		*q = (uint32_t)(*ma);
+	}
+
+	o = TIFFWriteDirectoryTagCheckedLongArray(tif, ndir, dir, tag, count, p);
+	_TIFFfree(p);
+
+	return(o);
 }
 
 #ifdef notdef
@@ -1577,15 +1621,64 @@ TIFFWriteDirectoryTagSlong8(TIFF* tif, uint32_t* ndir, TIFFDirEntry* dir, uint16
 }
 #endif
 
+/************************************************************************/
+/*                 TIFFWriteDirectoryTagSlong8Array()                   */
+/*                                                                      */
+/*      Write either SLong8 or SLong array depending on file type.      */
+/************************************************************************/
 static int
 TIFFWriteDirectoryTagSlong8Array(TIFF* tif, uint32_t* ndir, TIFFDirEntry* dir, uint16_t tag, uint32_t count, int64_t* value)
 {
+	static const char module[] = "TIFFWriteDirectoryTagSlong8Array";
+	int64_t *ma;
+	uint32_t mb;
+	int32_t *p;
+	int32_t *q;
+	int o;
+
+	/* is this just a counting pass? */
 	if (dir==NULL)
 	{
 		(*ndir)++;
 		return(1);
 	}
-	return(TIFFWriteDirectoryTagCheckedSlong8Array(tif,ndir,dir,tag,count,value));
+	/* We always write SLong8 for BigTIFF, no checking needed. */
+	if (tif->tif_flags & TIFF_BIGTIFF)
+		return(TIFFWriteDirectoryTagCheckedSlong8Array(tif, ndir, dir, tag, count, value));
+
+	/*
+	** For classic tiff we want to verify everything is in range for signed-long
+	** and convert to signed-long format.
+	*/
+	p = _TIFFmalloc(count * sizeof(uint32_t));
+	if (p == NULL)
+	{
+		TIFFErrorExt(tif->tif_clientdata, module, "Out of memory");
+		return(0);
+	}
+
+	for (q = p, ma = value, mb = 0; mb < count; ma++, mb++, q++)
+	{
+		if (*ma > (2147483647))
+		{
+			TIFFErrorExt(tif->tif_clientdata, module,
+				"Attempt to write signed long value %"PRIi64" larger than 0x7FFFFFFF (2147483647) for tag %d in Classic TIFF file. TIFF writing to file aborted", *ma, tag);
+			_TIFFfree(p);
+			return(0);
+		} else if (*ma < (-2147483647 - 1))
+		{
+			TIFFErrorExt(tif->tif_clientdata, module,
+				"Attempt to write signed long value %"PRIi64" smaller than 0x80000000 (-2147483648) for tag %d in Classic TIFF file. TIFF writing to file aborted", *ma, tag);
+			_TIFFfree(p);
+			return(0);
+		}
+		*q = (int32_t)(*ma);
+	}
+
+	o = TIFFWriteDirectoryTagCheckedSlongArray(tif, ndir, dir, tag, count, p);
+	_TIFFfree(p);
+
+	return(o);
 }
 
 static int