Commit ac9f64a1e for imagemagick.org

commit ac9f64a1e9eb7671d994e35132287594fd993cbb
Author: Greg B <64932474+gregbenz@users.noreply.github.com>
Date:   Sat Sep 5 17:26:33 2026 -0500

    fix: preserve destination on UHDR encoder failure (#8941)

diff --git a/Makefile.in b/Makefile.in
index bf451aea2..ddfde75e2 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -6276,6 +6276,7 @@ TESTS_TESTS = \
   tests/cli-pipe.tap \
   tests/cli-svg.tap \
   tests/cli-uhdr.tap \
+  tests/cli-uhdr-encoder.tap \
   tests/validate-colorspace.tap \
   tests/validate-compare.tap \
   tests/validate-composite.tap \
diff --git a/coders/uhdr.c b/coders/uhdr.c
index 4b1f8bd2c..ad7e71aa2 100644
--- a/coders/uhdr.c
+++ b/coders/uhdr.c
@@ -1286,15 +1286,17 @@ static MagickBooleanType WriteUHDRImage(const ImageInfo *image_info,
       resized_gainmap_profile=TransformGainMapProfile(image_info,image,
         gainmap_profile,&gainmap_transform_status,exception);
       if (gainmap_transform_status == MagickFalse)
-        return(MagickFalse);
+        {
+          status=MagickFalse;
+          goto cleanup;
+        }
       if (resized_gainmap_profile != (StringInfo *) NULL)
         gainmap_profile=(const StringInfo *) resized_gainmap_profile;
       base_image_profile=EncodeBaseImageProfile(image_info,image,exception);
       if (base_image_profile == (StringInfo *) NULL)
         {
-          if (resized_gainmap_profile != (StringInfo *) NULL)
-            resized_gainmap_profile=DestroyStringInfo(resized_gainmap_profile);
-          return(MagickFalse);
+          status=MagickFalse;
+          goto cleanup;
         }
       base_image.data=(void *) GetStringInfoDatum(base_image_profile);
       base_image.data_sz=GetStringInfoLength(base_image_profile);
@@ -1322,12 +1324,7 @@ static MagickBooleanType WriteUHDRImage(const ImageInfo *image_info,
       GetHDRGMProperty("HDRCapacityMax",hdr_capacity_max);
       GetHDRGMPropertyInt("UseBaseColorGrade",use_base_cg);
       if (status == MagickFalse)
-        {
-          if (resized_gainmap_profile != (StringInfo *) NULL)
-            resized_gainmap_profile=DestroyStringInfo(resized_gainmap_profile);
-          base_image_profile=DestroyStringInfo(base_image_profile);
-          return(MagickFalse);
-        }
+        goto cleanup;
       preserve_gainmap=MagickTrue;
     }

@@ -1341,16 +1338,8 @@ static MagickBooleanType WriteUHDRImage(const ImageInfo *image_info,
     {
       (void) ThrowMagickException(exception,GetMagickModule(),ConfigureWarning,
         "invalid hdr color transfer received, ","%s","exiting ... ");
-      return(MagickFalse);
-    }
-  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
-  if (status == MagickFalse)
-    {
-      if (resized_gainmap_profile != (StringInfo *) NULL)
-        resized_gainmap_profile=DestroyStringInfo(resized_gainmap_profile);
-      if (base_image_profile != (StringInfo *) NULL)
-        base_image_profile=DestroyStringInfo(base_image_profile);
-      return(status);
+      status=MagickFalse;
+      goto cleanup;
     }

   /*
@@ -1770,16 +1759,22 @@ next_image:
     {
       uhdr_compressed_image_t *output = uhdr_get_encoded_stream(handle);

-      (void) WriteBlob(image, output->data_sz, output->data);
+      status=OpenBlob(image_info,images,WriteBinaryBlobMode,exception);
+      if (status != MagickFalse)
+        {
+          if (WriteBlob(images,output->data_sz,output->data) !=
+              (ssize_t) output->data_sz)
+            status=MagickFalse;
+          if (CloseBlob(images) == MagickFalse)
+            status=MagickFalse;
+        }

       uhdr_release_encoder(handle);
     }
   }
 #undef CHECK_IF_ERR

-  if (CloseBlob(image) == MagickFalse)
-    status = MagickFalse;
-
+cleanup:
   if (hdrImgDescriptor.planes[UHDR_PLANE_Y])
     RelinquishMagickMemory(hdrImgDescriptor.planes[UHDR_PLANE_Y]);

diff --git a/tests/Makefile.am b/tests/Makefile.am
index c9e547caa..bf1ee4131 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -45,6 +45,7 @@ TESTS_TESTS = \
   tests/cli-pipe.tap \
   tests/cli-svg.tap \
   tests/cli-uhdr.tap \
+  tests/cli-uhdr-encoder.tap \
   tests/validate-colorspace.tap \
   tests/validate-compare.tap \
   tests/validate-composite.tap \
diff --git a/tests/cli-uhdr-encoder.tap b/tests/cli-uhdr-encoder.tap
new file mode 100755
index 000000000..02e35f002
--- /dev/null
+++ b/tests/cli-uhdr-encoder.tap
@@ -0,0 +1,99 @@
+#!/bin/sh
+# Regression tests for safe Ultra HDR encoder failures.
+. ./common.shi
+. ${srcdir}/tests/common.shi
+
+encoder_error=cli-uhdr-encoder-failure.jpg
+encoder_sdr=cli-uhdr-encoder-sdr.miff
+encoder_hdr=cli-uhdr-encoder-hdr.miff
+pattern_output=cli-uhdr-encoder-pattern-%d.jpg
+pattern_first=cli-uhdr-encoder-pattern-0.jpg
+pattern_wrong=cli-uhdr-encoder-pattern-1.jpg
+
+cleanup()
+{
+  rm -f "$encoder_error" "$encoder_sdr" "$encoder_hdr" \
+    "$pattern_first" "$pattern_wrong"
+}
+
+cleanup
+if ! ${MAGICK} -list configure 2>/dev/null | awk '
+    $1 == "DELEGATES" {
+      for (i=2; i <= NF; i++)
+        {
+          if ($i == "jpeg")
+            jpeg=1
+          if ($i == "uhdr")
+            uhdr=1
+        }
+    }
+    END { exit(jpeg && uhdr ? 0 : 1) }
+  '; then
+  echo "1..0 # SKIP JPEG or UHDR coder unavailable"
+  exit 0
+fi
+
+echo "1..6"
+if ! ${MAGICK} "${SRCDIR}/rose.pnm" -depth 8 "$encoder_sdr" \
+    >/dev/null 2>&1 ||
+   ! ${MAGICK} "${SRCDIR}/rose.pnm" -depth 16 "$encoder_hdr" \
+    >/dev/null 2>&1; then
+  echo "Bail out! unable to create encoder test inputs"
+  cleanup
+  exit 0
+fi
+
+printf 'existing encoder destination' > "$encoder_error"
+encoder_error_message=`${MAGICK} "$encoder_sdr" "$encoder_hdr" \
+  -define uhdr:hdr-color-transfer=linear \
+  -define uhdr:gainmap-quality=101 "UHDR:$encoder_error" 2>&1`
+encoder_error_status=$?
+if [ "$encoder_error_status" = "0" ]; then
+  echo "not ok - invalid encoder setting fails"
+else
+  echo "ok - invalid encoder setting fails"
+fi
+case "$encoder_error_message" in
+  *quality*101*) echo "ok - invalid setting reaches the UHDR encoder" ;;
+  *)
+    echo "not ok - invalid setting reaches the UHDR encoder"
+    echo "# got '$encoder_error_message'"
+    ;;
+esac
+encoder_error_contents=`cat "$encoder_error"`
+if [ "X$encoder_error_contents" = "Xexisting encoder destination" ]; then
+  echo "ok - encoder failure preserves its destination"
+else
+  echo "not ok - encoder failure preserves its destination"
+  echo "# expected 'existing encoder destination', got '$encoder_error_contents'"
+fi
+
+printf 'existing second scene' > "$pattern_wrong"
+pattern_status=0
+${MAGICK} "$encoder_sdr" "$encoder_hdr" \
+  -define uhdr:hdr-color-transfer=linear "UHDR:$pattern_output" \
+  >/dev/null 2>&1 || pattern_status=$?
+if [ "$pattern_status" = "0" ]; then
+  echo "ok - patterned output succeeds"
+else
+  echo "not ok - patterned output succeeds"
+fi
+pattern_decode=`${IDENTIFY} -quiet -format '%m|%[profiles]' \
+  "UHDR:$pattern_first" 2>/dev/null`
+case "$pattern_status:$pattern_decode" in
+  0:UHDR\|*hdrgm*)
+    echo "ok - patterned output writes a decodable UHDR to the first scene" ;;
+  *)
+    echo "not ok - patterned output writes a decodable UHDR to the first scene"
+    echo "# status=$pattern_status decode='$pattern_decode'"
+    ;;
+esac
+if printf 'existing second scene' | cmp -s - "$pattern_wrong"; then
+  echo "ok - patterned output preserves the second scene destination"
+else
+  echo "not ok - patterned output preserves the second scene destination"
+  pattern_wrong_size=`wc -c < "$pattern_wrong" 2>/dev/null`
+  echo "# destination changed or is missing (size=$pattern_wrong_size)"
+fi
+cleanup
+: