Commit f49157629 for imagemagick.org

commit f491576297141ff685dc4460d811986f5b6b6cec
Author: Dirk Lemstra <dirk@lemstra.org>
Date:   Fri Aug 28 13:16:22 2026 +0200

    Use a better algorithm to determine the numerator and denominator (GHSA-v45j-x8p4-3mh4)

diff --git a/MagickCore/profile.c b/MagickCore/profile.c
index fc046994e..38833b37c 100644
--- a/MagickCore/profile.c
+++ b/MagickCore/profile.c
@@ -2641,11 +2641,39 @@ static MagickBooleanType GetXmpOffsets(const StringInfo *profile,
   return(MagickTrue);
 }

+static MagickBooleanType TryAccumulate(const double term,
+  const unsigned long previous,const unsigned long before_previous,
+  unsigned long *result)
+{
+  double
+    accumulated;
+
+  *result=0;
+  if (term > (double) MAGICK_ULONG_MAX)
+    return(MagickFalse);
+  accumulated=(term*previous)+before_previous;
+  if ((accumulated < 0.0) || (accumulated > (double) MAGICK_ULONG_MAX))
+    return(MagickFalse);
+  *result=(unsigned long) accumulated;
+  return(MagickTrue);
+}
+
 static void GetXmpNumeratorAndDenominator(double value,
   unsigned long *numerator,unsigned long *denominator)
 {
   double
-    df;
+    remainder;
+
+  unsigned long
+    den,
+    new_den,
+    new_num,
+    num,
+    prev_den,
+    prev_num;
+
+  ssize_t
+    i;

   *numerator=0;
   *denominator=1;
@@ -2653,29 +2681,47 @@ static void GetXmpNumeratorAndDenominator(double value,
     return;
   if (value > (double) MAGICK_ULONG_MAX)
     {
-      *numerator = MAGICK_ULONG_MAX;
-      *denominator = 1;
+      *numerator=MAGICK_ULONG_MAX;
+      *denominator=1;
       return;
     }
   if (floor(value) == value)
     {
-      *numerator = (unsigned long) value;
-      *denominator = 1;
+      *numerator=(unsigned long) value;
+      *denominator=1;
       return;
     }
-  *numerator=1;
-  df=1.0;
-  while(fabs(df - value) > MagickEpsilon)
+  num=1;
+  prev_num=0;
+  den=0;
+  prev_den=1;
+  remainder=value;
+  for (i=0; i < 128; i++)
   {
-    if (df < value)
-      (*numerator)++;
-    else
-      {
-        (*denominator)++;
-        *numerator=(unsigned long) (value*(*denominator));
-      }
-    df=*numerator/(double)*denominator;
+    double
+      approximation,
+      fraction,
+      term;
+
+    term=floor(remainder);
+    if ((TryAccumulate(term,num,prev_num,&new_num) == MagickFalse) ||
+        (TryAccumulate(term,den,prev_den,&new_den) == MagickFalse) ||
+        (new_den == 0))
+      break;
+    prev_num=num;
+    num=new_num;
+    prev_den=den;
+    den=new_den;
+    approximation=num/(double) den;
+    if (fabs(approximation-value) <= MagickEpsilon)
+      break;
+    fraction=remainder-term;
+    if (fraction <= 0.0)
+      break;
+    remainder=1.0/fraction;
   }
+  *numerator=num;
+  *denominator=(den == 0 ? 1 : den);
 }

 static void SyncXmpProfile(const Image *image,StringInfo *profile)