Commit cafc7372c for imagemagick.org

commit cafc7372c8113a9636766f0eddde7b90373c9b44
Author: Cristy <urban-warrior@imagemagick.org>
Date:   Tue Sep 8 19:42:24 2026 -0400

    https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-2vwg-3g4g-qprm

diff --git a/MagickCore/distribute-cache.c b/MagickCore/distribute-cache.c
index 62c6158d8..6fb387564 100644
--- a/MagickCore/distribute-cache.c
+++ b/MagickCore/distribute-cache.c
@@ -156,7 +156,7 @@ static size_t
 */

 #if !defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
-static inline MagickOffsetType dpc_read(SOCKET_TYPE magick_unused(file),
+static inline MagickOffsetType dpc_receive(SOCKET_TYPE magick_unused(file),
   const MagickSizeType magick_unused(length),
   unsigned char *magick_restrict magick_unused(message))
 {
@@ -165,25 +165,118 @@ static inline MagickOffsetType dpc_read(SOCKET_TYPE magick_unused(file),
   magick_unreferenced(message);
   return(-1);
 }
+
+static inline MagickOffsetType dpc_receive_deadline(SOCKET_TYPE
+  magick_unused(file),const MagickSizeType magick_unused(length),
+  unsigned char *magick_restrict magick_unused(message),
+  double magick_unused(deadline))
+{
+  magick_unreferenced(file);
+  magick_unreferenced(length);
+  magick_unreferenced(message);
+  magick_unreferenced(deadline);
+  return(-1);
+}
 #else
-static inline MagickOffsetType dpc_read(SOCKET_TYPE file,
+static void SetSocketReceiveTimeout(SOCKET_TYPE file,double seconds)
+{
+  /*
+    Set a receive timeout.
+  */
+#if defined(MAGICKCORE_HAVE_WINSOCK2)
+  {
+    DWORD timeout = (DWORD) (seconds*1000.0);
+    (void) setsockopt(file,SOL_SOCKET,SO_RCVTIMEO,
+      (const char *) &timeout,sizeof(timeout));
+  }
+#else
+  {
+    struct timeval tv;
+    tv.tv_sec=(int) seconds;
+    tv.tv_usec=(int) ((seconds-(double) tv.tv_sec)*1000000.0);
+    (void) setsockopt(file,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));
+  }
+#endif
+}
+
+static inline MagickOffsetType dpc_receive(SOCKET_TYPE file,
   const MagickSizeType length,unsigned char *magick_restrict message)
 {
   MagickOffsetType offset = 0;
   while (offset < (MagickOffsetType) length)
   {
-    MagickSizeType remaining = length-(MagickSizeType) offset;
-    ssize_t count = recv(file,(char *) message+offset,(LENGTH_TYPE)
-      MagickMin(remaining,(MagickSizeType) MagickMaxBufferExtent),0);
+    size_t chunk = MagickMin(length-offset,(size_t) MagickMaxBufferExtent);
+    ssize_t count = recv(file,(char *) message+offset,(LENGTH_TYPE) chunk,0);
     if (count > 0)
       offset+=(MagickOffsetType) count;
     else
       if (count == 0)
-        break;
+        return(0);
+      else
+        {
+#ifdef MAGICKCORE_HAVE_WINSOCK2
+          int wsa_err = WSAGetLastError();
+          if ((wsa_err == WSAEINTR) || (wsa_err == WSAEWOULDBLOCK))
+            continue;
+          if (wsa_err == WSAECONNRESET)
+            return(-2);
+#else
+          if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
+            continue;
+          if (errno == ECONNRESET)
+            return(-2);
+#endif
+          return(-1);
+        }
+  }
+  return(offset);
+}
+
+static inline MagickOffsetType dpc_receive_deadline(SOCKET_TYPE file,
+  MagickSizeType length,unsigned char *message,double deadline)
+{
+  /*
+    Enforce an absolute deadline for authentication reads.
+  */
+  MagickOffsetType offset = 0;
+  while (offset < (MagickOffsetType) length)
+  {
+    double
+      now = (double) time((time_t *) NULL),
+      remaining = deadline-now;
+
+    size_t
+      chunk;
+
+    ssize_t
+      count;
+
+    if (now >= deadline)
+      return(-1);
+    if (remaining < 0.001)
+      remaining=0.001;
+    SetSocketReceiveTimeout(file,remaining);
+    chunk=MagickMin(length-offset,(size_t) MagickMaxBufferExtent);
+    count=recv(file,(char *) message+offset,(LENGTH_TYPE) chunk,0);
+    if (count > 0)
+      offset+=(MagickOffsetType) count;
+    else
+      if (count == 0)
+        return(0);
       else
         {
-          if (errno != EINTR)
-            break;
+#ifdef _WIN32
+          int wsa_err = WSAGetLastError();
+          if ((wsa_err == WSAEINTR) || (wsa_err == WSAEWOULDBLOCK))
+            continue;
+          if (wsa_err == WSAECONNRESET)
+            return(-2);
+          return(-1);
+#else
+          if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
+            continue;
+          return(-1);
+#endif
         }
   }
   return(offset);
@@ -314,7 +407,7 @@ static inline void DeriveSIPKeyFromSecret(const char *shared_secret,
   for (i=0; i < length; i++)
   {
     uint8_t
-      b = shared_secret[i];
+      b = (uint8_t) shared_secret[i];

     k0^=b;
     k0*=0x100000001b3ULL;
@@ -500,7 +593,7 @@ static char *GetHostname(int *port,ExceptionInfo *exception)
   if (hostlist[2] == (char *) NULL)
     *port=DPCPort;
   else
-    *port=StringToLong(hostlist[2]);
+    *port=(int) StringToLong(hostlist[2]);
   for (i=0; i < (ssize_t) argc; i++)
     hostlist[i]=DestroyString(hostlist[i]);
   hostlist=(char **) RelinquishMagickMemory(hostlist);
@@ -634,7 +727,7 @@ static inline MagickOffsetType dpc_send(SOCKET_TYPE file,
   count=0;
   for (i=0; i < (MagickOffsetType) length; i+=count)
   {
-    count=(ssize_t) send(file,(char *) message+i,(LENGTH_TYPE) MagickMin(
+    count=(ssize_t) send(file,(const char *) message+i,(LENGTH_TYPE) MagickMin(
       length-(MagickSizeType) i,(MagickSizeType) MagickMaxBufferExtent),
       MSG_NOSIGNAL);
     if (count <= 0)
@@ -701,7 +794,7 @@ static MagickBooleanType OpenDistributeCache(SplayTreeInfo *registry,
     sizeof(image->alpha_trait)+sizeof(image->channels)+sizeof(image->columns)+
     sizeof(image->rows)+sizeof(image->number_channels)+MaxPixelChannels*
     sizeof(*image->channel_map)+sizeof(image->metacontent_extent);
-  count=dpc_read(file,length,message);
+  count=dpc_receive(file,length,message);
   if (count != (MagickOffsetType) length)
     {
       image=DestroyImage(image);
@@ -800,7 +893,7 @@ static MagickBooleanType ReadDistributeCacheMetacontent(SplayTreeInfo *registry,
     return(MagickFalse);
   length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
     sizeof(region.y)+sizeof(length);
-  count=dpc_read(file,length,message);
+  count=dpc_receive(file,length,message);
   if (count != (MagickOffsetType) length)
     return(MagickFalse);
   q=message;
@@ -864,7 +957,7 @@ static MagickBooleanType ReadDistributeCachePixels(SplayTreeInfo *registry,
     return(MagickFalse);
   length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
     sizeof(region.y)+sizeof(length);
-  count=dpc_read(file,length,message);
+  count=dpc_receive(file,length,message);
   if (count != (MagickOffsetType) length)
     return(MagickFalse);
   q=message;
@@ -934,7 +1027,7 @@ static MagickBooleanType WriteDistributeCacheMetacontent(
     return(MagickFalse);
   length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
     sizeof(region.y)+sizeof(length);
-  count=dpc_read(file,length,message);
+  count=dpc_receive(file,length,message);
   if (count != (MagickOffsetType) length)
     return(MagickFalse);
   p=message;
@@ -956,7 +1049,7 @@ static MagickBooleanType WriteDistributeCacheMetacontent(
   if (q == (Quantum *) NULL)
     return(MagickFalse);
   metacontent=(unsigned char *) GetAuthenticMetacontent(image);
-  count=dpc_read(file,length,metacontent);
+  count=dpc_receive(file,length,metacontent);
   if (count != (MagickOffsetType) length)
     return(MagickFalse);
   return(SyncAuthenticPixels(image,exception));
@@ -998,7 +1091,7 @@ static MagickBooleanType WriteDistributeCachePixels(SplayTreeInfo *registry,
     return(MagickFalse);
   length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
     sizeof(region.y)+sizeof(length);
-  count=dpc_read(file,length,message);
+  count=dpc_receive(file,length,message);
   if (count != (MagickOffsetType) length)
     return(MagickFalse);
   p=message;
@@ -1019,7 +1112,7 @@ static MagickBooleanType WriteDistributeCachePixels(SplayTreeInfo *registry,
     exception);
   if (q == (Quantum *) NULL)
     return(MagickFalse);
-  count=dpc_read(file,length,(unsigned char *) q);
+  count=dpc_receive(file,length,(unsigned char *) q);
   if (count != (MagickOffsetType) length)
     return(MagickFalse);
   return(SyncAuthenticPixels(image,exception));
@@ -1120,7 +1213,8 @@ static HANDLER_RETURN_TYPE DistributePixelCacheClient(void *socket_arg)
   /*
     Receive client's keyed hash.
   */
-  count=dpc_read(client_socket,sizeof(key),(unsigned char *) &key);
+  count=dpc_receive_deadline(client_socket,sizeof(key),(unsigned char *) &key,
+    (double) time((time_t *) NULL)+5.0);
   if ((count != (MagickOffsetType) sizeof(key)) || (key != session_key))
     {
       CLOSE_SOCKET(client_socket);
@@ -1140,10 +1234,10 @@ static HANDLER_RETURN_TYPE DistributePixelCacheClient(void *socket_arg)
     /*
       Each command must echo the authenticated session key.
     */
-    count=dpc_read(client_socket,1,(unsigned char *) &command);
+    count=dpc_receive(client_socket,1,(unsigned char *) &command);
     if (count <= 0)
       break;
-    count=dpc_read(client_socket,sizeof(key),(unsigned char *) &key);
+    count=dpc_receive(client_socket,sizeof(key),(unsigned char *) &key);
     if ((count != (MagickOffsetType) sizeof(key)) || (key != session_key))
       break;
     switch (command)
@@ -1333,24 +1427,12 @@ MagickExport void DistributePixelCacheServer(const int port,
       continue;
     status=setsockopt(server_socket,SOL_SOCKET,SO_REUSEADDR,(char *) &one,
       (socklen_t) sizeof(one));
-    if (status != -1)
-      {
-#if defined(MAGICKCORE_HAVE_WINSOCK2)
-        DWORD timeout = 5000; // ms
-        status=setsockopt(server_socket,SOL_SOCKET,SO_RCVTIMEO,(const char *)
-          &timeout,sizeof(timeout));
-#else
-        struct timeval tv;
-        tv.tv_sec=5;
-        tv.tv_usec=0;
-        status=setsockopt(server_socket,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));
-#endif
-      }
     if (status == -1)
       {
         CLOSE_SOCKET(server_socket);
         continue;
       }
+    SetSocketReceiveTimeout(server_socket,5.0);
     status=bind(server_socket,p->ai_addr,(socklen_t) p->ai_addrlen);
     if (status == -1)
       {
@@ -1395,25 +1477,7 @@ MagickExport void DistributePixelCacheServer(const int port,
           client_socket_ptr);
         continue;
       }
-#if defined(MAGICKCORE_HAVE_WINSOCK2)
-    {
-      DWORD
-        timeout = 5000;
-
-      status=setsockopt(*client_socket_ptr,SOL_SOCKET,SO_RCVTIMEO,
-        (const char *) &timeout,sizeof(timeout));
-    }
-#else
-    {
-      struct timeval
-        tv;
-
-      tv.tv_sec=5;
-      tv.tv_usec=0;
-      status=setsockopt(*client_socket_ptr,SOL_SOCKET,SO_RCVTIMEO,&tv,
-        sizeof(tv));
-    }
-#endif
+    SetSocketReceiveTimeout(*client_socket_ptr,5.0);
     if (status == -1)
       {
         CLOSE_SOCKET(*client_socket_ptr);
@@ -1648,7 +1712,7 @@ MagickPrivate MagickBooleanType OpenDistributePixelCache(
   if (count != (MagickOffsetType) (p-message))
     return(MagickFalse);
   status=MagickFalse;
-  count=dpc_read(server_info->file,sizeof(status),(unsigned char *) &status);
+  count=dpc_receive(server_info->file,sizeof(status),(unsigned char *) &status);
   if (count != (MagickOffsetType) sizeof(status))
     return(MagickFalse);
   return(status);
@@ -1724,7 +1788,7 @@ MagickPrivate MagickOffsetType ReadDistributePixelCacheMetacontent(
   count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
   if (count != (MagickOffsetType) (p-message))
     return(-1);
-  return(dpc_read(server_info->file,length,metacontent));
+  return(dpc_receive(server_info->file,length,metacontent));
 }

 /*
@@ -1797,7 +1861,7 @@ MagickPrivate MagickOffsetType ReadDistributePixelCachePixels(
   count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
   if (count != (MagickOffsetType) (p-message))
     return(-1);
-  return(dpc_read(server_info->file,length,pixels));
+  return(dpc_receive(server_info->file,length,pixels));
 }

 /*
@@ -1850,7 +1914,7 @@ MagickPrivate MagickBooleanType RelinquishDistributePixelCache(
   if (count != (MagickOffsetType) (p-message))
     return(MagickFalse);
   status=MagickFalse;
-  count=dpc_read(server_info->file,sizeof(status),(unsigned char *) &status);
+  count=dpc_receive(server_info->file,sizeof(status),(unsigned char *) &status);
   if (count != (MagickOffsetType) sizeof(status))
     return(MagickFalse);
   return(status);