Commit 282f455de for imagemagick.org

commit 282f455de5c80a7a0d1a713087db9c8fce344141
Author: Cristy <urban-warrior@imagemagick.org>
Date:   Sat Aug 29 11:54:46 2026 -0400

    https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-3rjr-534c-8v67

diff --git a/MagickCore/blob-private.h b/MagickCore/blob-private.h
index 5e868c4f9..a16221ee4 100644
--- a/MagickCore/blob-private.h
+++ b/MagickCore/blob-private.h
@@ -148,6 +148,9 @@ extern MagickExport void
   MSBOrderLong(unsigned char *,const size_t),
   MSBOrderShort(unsigned char *,const size_t);

+extern MagickPrivate void
+  SyncImagesBlob(ImageInfo *,const Image *,Image *);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
diff --git a/MagickCore/blob.c b/MagickCore/blob.c
index 21fbf4c23..5b94733ba 100644
--- a/MagickCore/blob.c
+++ b/MagickCore/blob.c
@@ -2109,8 +2109,9 @@ MagickExport void *ImageToBlob(const ImageInfo *image_info,
               else
                 blob=ResizeQuantumMemory(blob,*length+1,sizeof(unsigned char));
             }
-          else if ((status == MagickFalse) && (image->blob->extent == 0))
-            blob_info->blob=RelinquishMagickMemory(blob_info->blob);
+          else
+            if ((status == MagickFalse) && (image->blob->extent == 0))
+              blob_info->blob=RelinquishMagickMemory(blob_info->blob);
         }
     }
   else
@@ -2504,6 +2505,7 @@ MagickExport void *ImagesToBlob(const ImageInfo *image_info,Image *images,
           images->blob->extent=0;
           *images->filename='\0';
           status=WriteImages(blob_info,images,images->filename,exception);
+          SyncImagesBlob(blob_info,images,images);
           *length=images->blob->length;
           blob=DetachBlob(images->blob);
           if (blob != (void *) NULL)
@@ -5630,6 +5632,73 @@ static int SyncBlob(const Image *image)
 %                                                                             %
 %                                                                             %
 %                                                                             %
++  S y n c I m a g e s B l o b                                                %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  SyncImagesBlob() re-synchronizes the in-memory blob stream that is shared
+%  by an image list.  An encoder may grow the blob with SetBlobExtent(), which
+%  reallocates (and therefore frees) the buffer that AttachBlob() previously
+%  handed to the other images of the list and to image_info->blob.  This method
+%  replaces every stale reference to that buffer with the current one so that a
+%  subsequent AttachBlob(), write, or free never touches freed memory.
+%
+%  The format of the SyncImagesBlob method is:
+%
+%      void SyncImagesBlob(ImageInfo *image,const Image *image,Image *images)
+%
+%  A description of each parameter follows:
+%
+%    o image_info: the image info whose blob member is updated; may be NULL.
+%
+%    o image: the image that was just written (owner of the live blob).
+%
+%    o images: the head of the image list that is being synchronized.
+%
+*/
+MagickPrivate void SyncImagesBlob(ImageInfo *image_info,const Image *image,
+  Image *images)
+{
+  Image
+    *p;
+
+  unsigned char
+    *current,
+    *stale;
+
+  assert(image != (const Image *) NULL);
+  assert(image->signature == MagickCoreSignature);
+  if ((image->blob == (BlobInfo *) NULL) || (image->blob->type != BlobStream))
+    return;
+  current=image->blob->data;
+  stale=(unsigned char *) (image_info != (ImageInfo *) NULL ?
+    image_info->blob : (void *) NULL);
+  if (image_info != (ImageInfo *) NULL)
+    {
+      image_info->blob=(void *) current;
+      image_info->length=image->blob->length;
+    }
+  if ((stale == (unsigned char *) NULL) || (stale == current))
+    return;  /* nothing was reallocated */
+  for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
+  {
+    if ((p == image) || (p->blob == (BlobInfo *) NULL))
+      continue;
+    if ((p->blob->type != BlobStream) || (p->blob->data != stale))
+      continue;
+    p->blob->data=current;
+    p->blob->extent=image->blob->extent;
+    p->blob->length=image->blob->length;
+  }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
 +  T e l l B l o b                                                            %
 %                                                                             %
 %                                                                             %
diff --git a/MagickCore/constitute.c b/MagickCore/constitute.c
index 60ea47bfd..a2c653002 100644
--- a/MagickCore/constitute.c
+++ b/MagickCore/constitute.c
@@ -1592,6 +1592,8 @@ MagickExport MagickBooleanType WriteImages(const ImageInfo *image_info,
       progress_monitor=SetImageProgressMonitor(p,(MagickProgressMonitor) NULL,
         p->client_data);
     status&=(MagickStatusType) WriteImage(write_info,p,exception);
+    if (write_info->blob != (void *) NULL)
+      SyncImagesBlob(write_info,p,images);
     if (number_images != 1)
       (void) SetImageProgressMonitor(p,progress_monitor,p->client_data);
     if (write_info->adjoin != MagickFalse)