Commit a5c0257c3 for imagemagick.org

commit a5c0257c30a932e74620b4ad937adaadeeb2a3c0
Author: Cristy <urban-warrior@imagemagick.org>
Date:   Sun Nov 15 11:12:43 2026 -0500

    fixed a rare but possible use after free

diff --git a/MagickCore/fx.c b/MagickCore/fx.c
index a165e961d..792753032 100644
--- a/MagickCore/fx.c
+++ b/MagickCore/fx.c
@@ -833,6 +833,15 @@ static MagickBooleanType InitFx (FxInfo * pfx, const Image * img,
   }

   pfx->Images = ImageListToArray (img, pfx->exception);
+  if (pfx->Images == (Image **) NULL) {
+    for ( ; i > 0; i--) {
+      ImgT * pimg = &pfx->Imgs[i-1];
+      pimg->View = DestroyCacheView (pimg->View);
+    }
+    pfx->Imgs=(ImgT *) RelinquishMagickMemory (pfx->Imgs);
+    pfx->random_infos = DestroyRandomInfoTLS (pfx->random_infos);
+    return MagickFalse;
+  }

   return MagickTrue;
 }
@@ -924,15 +933,28 @@ static int FindUserSymbol (FxInfo * pfx, char * name)

 static MagickBooleanType ExtendUserSymbols (FxInfo * pfx)
 {
-  pfx->numUserSymbols = (int) ceil (pfx->numUserSymbols * (1 + TableExtend));
-  pfx->UserSymbols = (UserSymbolT*) ResizeMagickMemory (pfx->UserSymbols, (size_t) pfx->numUserSymbols * sizeof(UserSymbolT));
-  if (!pfx->UserSymbols) {
+  UserSymbolT
+    *new_symbols;
+
+  int
+    symbol_count;
+
+  symbol_count = (int) ceil (pfx->numUserSymbols * (1 + TableExtend));
+  new_symbols = (UserSymbolT *) AcquireQuantumMemory ((size_t) symbol_count,
+    sizeof (*new_symbols));
+  if (new_symbols == (UserSymbolT *) NULL) {
     (void) ThrowMagickException (
       pfx->exception, GetMagickModule(), ResourceLimitFatalError,
       "UserSymbols", "%i",
-      pfx->numUserSymbols);
+      symbol_count);
     return MagickFalse;
   }
+  if (pfx->usedUserSymbols > 0)
+    (void) memcpy (new_symbols, pfx->UserSymbols,
+      (size_t) pfx->usedUserSymbols * sizeof (*new_symbols));
+  pfx->UserSymbols = (UserSymbolT *) RelinquishMagickMemory (pfx->UserSymbols);
+  pfx->UserSymbols = new_symbols;
+  pfx->numUserSymbols = symbol_count;

   return MagickTrue;
 }
@@ -940,10 +962,10 @@ static MagickBooleanType ExtendUserSymbols (FxInfo * pfx)
 static int AddUserSymbol (FxInfo * pfx, char * pex, size_t len)
 {
   UserSymbolT *pus;
-  if (++pfx->usedUserSymbols >= pfx->numUserSymbols) {
+  if (pfx->usedUserSymbols + 1 >= pfx->numUserSymbols) {
     if (!ExtendUserSymbols (pfx)) return -1;
   }
-  pus = &pfx->UserSymbols[pfx->usedUserSymbols-1];
+  pus = &pfx->UserSymbols[pfx->usedUserSymbols++];
   pus->pex = pex;
   pus->len = len;

@@ -961,11 +983,11 @@ static void DumpTables (FILE * fh)
     if (i >= (int) FirstImgAttr && i < aNull) str = ImgAttrs[i-(int) FirstImgAttr].str;
     if (i >= (int) FirstSym     && i < sNull) str = Symbols[i-(int) FirstSym].str;
     if (i >= (int) FirstCont    && i < rNull) str = Controls[i-(int) FirstCont].str;
-    if      (i==0    ) fprintf (stderr, "Operators:\n ");
-    else if (i==oNull) fprintf (stderr, "\nFunctions:\n ");
-    else if (i==fNull) fprintf (stderr, "\nImage attributes:\n ");
-    else if (i==aNull) fprintf (stderr, "\nSymbols:\n ");
-    else if (i==sNull) fprintf (stderr, "\nControls:\n ");
+    if      (i==0    ) fprintf (fh, "Operators:\n ");
+    else if (i==oNull) fprintf (fh, "\nFunctions:\n ");
+    else if (i==fNull) fprintf (fh, "\nImage attributes:\n ");
+    else if (i==aNull) fprintf (fh, "\nSymbols:\n ");
+    else if (i==sNull) fprintf (fh, "\nControls:\n ");
     fprintf (fh, " %s", str);
   }
   fprintf (fh, "\n");
@@ -1137,8 +1159,8 @@ static MagickBooleanType DumpRPN (FxInfo * pfx, FILE * fh)
   fprintf (fh, "  NeedStats=%s", pfx->NeedStats ? "yes" : "no");
   fprintf (fh, "  GotStats=%s", pfx->GotStats ? "yes" : "no");
   fprintf (fh, "  NeedHsl=%s\n", pfx->NeedHsl ? "yes" : "no");
-  if      (pfx->runType==rtEntireImage) fprintf (stderr, "EntireImage");
-  else if (pfx->runType==rtCornerOnly)  fprintf (stderr, "CornerOnly");
+  if      (pfx->runType==rtEntireImage) fprintf (fh, "EntireImage");
+  else if (pfx->runType==rtCornerOnly)  fprintf (fh, "CornerOnly");
   fprintf (fh, "\n");


@@ -1181,8 +1203,8 @@ static MagickBooleanType DumpRPN (FxInfo * pfx, FILE * fh)
       fprintf (fh, " ia=%s", OprStr((int) pel->img_attr_qual));

     if (pel->channel_qual != NO_CHAN_QUAL) {
-      if (pel->channel_qual == THIS_CHANNEL) fprintf (stderr, "  ch=this");
-      else fprintf (stderr, "  ch=%i", pel->channel_qual);
+      if (pel->channel_qual == THIS_CHANNEL) fprintf (fh, "  ch=this");
+      else fprintf (fh, "  ch=%i", pel->channel_qual);
     }

     if (pel->operator_index == rCopyTo) {
@@ -1236,7 +1258,7 @@ static size_t GetToken (FxInfo * pfx)
   size_t len = 0;
   *pfx->token = '\0';
   pfx->lenToken = 0;
-  if (!isalpha((int)*p)) return 0;
+  if (!isalpha((int) ((unsigned char) *p))) return 0;

   /* Regard strings that start "icc-" or "device-",
      followed by any number of alphas,
@@ -1246,26 +1268,26 @@ static size_t GetToken (FxInfo * pfx)
   if (LocaleNCompare (p, "icc-", 4) == 0) {
     len = 4;
     p += 4;
-    while (isalpha ((int)*p)) { len++; p++; }
+    while (isalpha ((int) ((unsigned char) *p))) { len++; p++; }
   } else if (LocaleNCompare (p, "device-", 7) == 0) {
     len = 7;
     p += 7;
-    while (isalpha ((int)*p)) { len++; p++; }
+    while (isalpha ((int) ((unsigned char) *p))) { len++; p++; }
   } else {
-    while (isalpha ((int)*p)) { len++; p++; }
-    if (*p == '_')            { len++; p++; }
-    while (isalpha ((int)*p)) { len++; p++; }
-    while (isdigit ((int)*p)) { len++; p++; }
+    while (isalpha ((int) ((unsigned char) *p))) { len++; p++; }
+    if (*p == '_')                              { len++; p++; }
+    while (isalpha ((int) ((unsigned char) *p))) { len++; p++; }
+    while (isdigit ((int) ((unsigned char) *p))) { len++; p++; }
   }
   if (len >= MaxTokenLen) {
     (void) ThrowMagickException (
       pfx->exception, GetMagickModule(), OptionError,
       "GetToken: too long", "%g at '%s'",
       (double) len, SetShortExp(pfx));
-    len = MaxTokenLen;
+    len = MaxTokenLen - 1;
   }
   if (len) {
-    (void) CopyMagickString (pfx->token, pfx->pex, (len+1<MaxTokenLen)?len+1:MaxTokenLen);
+    (void) CopyMagickString (pfx->token, pfx->pex, len+1);
   }

   pfx->lenToken = strlen (pfx->token);
@@ -1429,25 +1451,38 @@ static int MaybeXYWH (FxInfo * pfx, ImgAttrE * pop)

 static MagickBooleanType ExtendOperatorStack (FxInfo * pfx)
 {
-  pfx->numOprStack = (int) ceil (pfx->numOprStack * (1 + TableExtend));
-  pfx->OperatorStack = (OperatorE*) ResizeMagickMemory (pfx->OperatorStack, (size_t) pfx->numOprStack * sizeof(OperatorE));
-  if (!pfx->OperatorStack) {
+  OperatorE
+    *new_stack;
+
+  int
+    stack_count;
+
+  stack_count = (int) ceil (pfx->numOprStack * (1 + TableExtend));
+  new_stack = (OperatorE *) AcquireQuantumMemory ((size_t) stack_count,
+    sizeof (*new_stack));
+  if (new_stack == (OperatorE *) NULL) {
     (void) ThrowMagickException (
       pfx->exception, GetMagickModule(), ResourceLimitFatalError,
       "OprStack", "%i",
-      pfx->numOprStack);
+      stack_count);
     return MagickFalse;
   }
+  if (pfx->usedOprStack > 0)
+    (void) memcpy (new_stack, pfx->OperatorStack,
+      (size_t) pfx->usedOprStack * sizeof (*new_stack));
+  pfx->OperatorStack = (OperatorE *) RelinquishMagickMemory (pfx->OperatorStack);
+  pfx->OperatorStack = new_stack;
+  pfx->numOprStack = stack_count;
   return MagickTrue;
 }

 static MagickBooleanType PushOperatorStack (FxInfo * pfx, int op)
 {
-  if (++pfx->usedOprStack >= pfx->numOprStack) {
+  if (pfx->usedOprStack + 1 >= pfx->numOprStack) {
     if (!ExtendOperatorStack (pfx))
       return MagickFalse;
   }
-  pfx->OperatorStack[pfx->usedOprStack-1] = (OperatorE) op;
+  pfx->OperatorStack[pfx->usedOprStack++] = (OperatorE) op;

   if (pfx->maxUsedOprStack < pfx->usedOprStack)
     pfx->maxUsedOprStack = pfx->usedOprStack;
@@ -1683,7 +1718,6 @@ static ssize_t GetProperty (FxInfo * pfx, fxFltType *val, fxFltType *seconds)
       } else {
         *val = strtold (text, &tailptr);
         if (text == tailptr) {
-          text = DestroyString(text);
           (void) ThrowMagickException (
             pfx->exception, GetMagickModule(), OptionError,
             "Property", "'%s' text '%s' is not a number at '%s'",
@@ -1722,6 +1756,10 @@ static inline ssize_t GetConstantColour (FxInfo * pfx, fxFltType *v0, fxFltType
     IsDev;

   char ColSp[MagickPathExtent];
+  if (pfx->lenToken == 0) {
+    dummy_exception = DestroyExceptionInfo (dummy_exception);
+    return 0;
+  }
   (void) CopyMagickString (ColSp, pfx->token, MaxTokenLen);
   p = ColSp + pfx->lenToken - 1;
   if (*p == 'a' || *p == 'A') *p = '\0';
@@ -2427,6 +2465,7 @@ static MagickBooleanType GetOperand (
           pfx->exception, GetMagickModule(), OptionError,
           "Function", "'%s' not permitted at '%s'",
           pfx->token, SetShortExp(pfx));
+        return MagickFalse;
       }

       if (fe == fDo || fe == fFor || fe == fIf || fe == fWhile) {
@@ -2505,7 +2544,7 @@ static MagickBooleanType GetOperand (
       if (artifact != (const char *) NULL) {
         char * tailptr;
         fxFltType val = strtold (artifact, &tailptr);
-        if (pfx->token == tailptr) {
+        if (artifact == tailptr) {
           (void) ThrowMagickException (
             pfx->exception, GetMagickModule(), OptionError,
             "Artifact", "'%s' has value '%s', not a number, at '%s'",
@@ -2525,8 +2564,9 @@ static MagickBooleanType GetOperand (
       *UserSymNdx = FindUserSymbol (pfx, pfx->token);
       if (*UserSymNdx == NULL_ADDRESS) {
         *UserSymNdx = AddUserSymbol (pfx, pfx->pex, pfx->lenToken);
+        if (*UserSymNdx < 0)
+          return MagickFalse;
         *NewUserSymbol = MagickTrue;
-      } else {
       }
       pfx->pex += pfx->lenToken;

@@ -3027,6 +3067,14 @@ static MagickBooleanType CollectStatistics (FxInfo * pfx)

   for (;;) {
     pfx->statistics[imgNum] = CollectOneImgStats (pfx, img);
+    if (pfx->statistics[imgNum] == (ChannelStatistics *) NULL) {
+      while (imgNum > 0) {
+        imgNum--;
+        pfx->statistics[imgNum] = (ChannelStatistics *) RelinquishMagickMemory (pfx->statistics[imgNum]);
+      }
+      pfx->statistics = (ChannelStatistics **) RelinquishMagickMemory (pfx->statistics);
+      return MagickFalse;
+    }

     if (++imgNum == pfx->ImgListLen) break;
     img = GetNextImageInList (img);
@@ -3103,7 +3151,7 @@ static inline fxFltType ImageStat (
       ret = (fxFltType) GetImageDepth (pfx->Images[ImgNum], pfx->exception);
       break;
     case aExtent:
-      ret = (fxFltType) GetBlobSize (pfx->image);
+      ret = (fxFltType) GetBlobSize (pfx->Images[ImgNum]);
       break;
     case aKurtosis:
       if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
@@ -3365,6 +3413,7 @@ static MagickBooleanType ExecuteRPN (FxInfo * pfx, fxRtT * pfxrt, fxFltType *res
       (void) ThrowMagickException (
         pfx->exception, GetMagickModule(), OptionError,
         "Bad run-time address", "%i", i);
+      break;
     }
     pel=&pfx->Elements[i];
     switch (pel->number_args) {
@@ -3413,7 +3462,13 @@ static MagickBooleanType ExecuteRPN (FxInfo * pfx, fxRtT * pfxrt, fxFltType *res
           regA = (pfxrt->UserSymVals[pel->element_index] *= regA);
           break;
         case oDivideEq:
-          regA = (pfxrt->UserSymVals[pel->element_index] /= regA);
+          if (fabs((double) regA) < MagickEpsilon) {
+            (void) ThrowMagickException (pfx->exception, GetMagickModule(),
+              OptionError, "DivideByZero", "`%s'", SetShortExp(pfx));
+            regA = (fxFltType) 0.0;
+          } else {
+            regA = (pfxrt->UserSymVals[pel->element_index] /= regA);
+          }
           break;
         case oPlusPlus:
           regA = pfxrt->UserSymVals[pel->element_index]++;
@@ -3431,11 +3486,26 @@ static MagickBooleanType ExecuteRPN (FxInfo * pfx, fxRtT * pfxrt, fxFltType *res
           regA *= regB;
           break;
         case oDivide:
-          regA /= regB;
+          if (fabs((double) regB) < MagickEpsilon) {
+            (void) ThrowMagickException (pfx->exception, GetMagickModule(),
+              OptionError, "DivideByZero", "`%s'", SetShortExp(pfx));
+            regA = (fxFltType) 0.0;
+          } else {
+            regA /= regB;
+          }
           break;
         case oModulus:
-          regA = fmod ((double) regA, fabs(floor((double) regB+0.5)));
-          break;
+          {
+            double mod_val = fabs(floor((double) regB+0.5));
+            if (mod_val < MagickEpsilon) {
+              (void) ThrowMagickException (pfx->exception, GetMagickModule(),
+                OptionError, "DivideByZero", "`%s'", SetShortExp(pfx));
+              regA = (fxFltType) 0.0;
+            } else {
+              regA = fmod ((double) regA, mod_val);
+            }
+            break;
+          }
         case oUnaryPlus:
           /* Do nothing. */
           break;
@@ -3443,7 +3513,7 @@ static MagickBooleanType ExecuteRPN (FxInfo * pfx, fxRtT * pfxrt, fxFltType *res
           regA = -regA;
           break;
         case oLshift:
-          if (CastDoubleToSizeT((double) regB+0.5) >= (8*sizeof(size_t)))
+          if ((regB < -0.5) || (CastDoubleToSizeT((double) regB+0.5) >= (8*sizeof(size_t))))
             {
               (void) ThrowMagickException ( pfx->exception, GetMagickModule(),
                 OptionError, "undefined shift", "%g", (double) regB);
@@ -3453,7 +3523,7 @@ static MagickBooleanType ExecuteRPN (FxInfo * pfx, fxRtT * pfxrt, fxFltType *res
           regA = (fxFltType) (CastDoubleToSizeT((double) regA+0.5) << CastDoubleToSizeT((double) regB+0.5));
           break;
         case oRshift:
-          if (CastDoubleToSizeT((double) regB+0.5) >= (8*sizeof(size_t)))
+          if ((regB < -0.5) || (CastDoubleToSizeT((double) regB+0.5) >= (8*sizeof(size_t))))
             {
               (void) ThrowMagickException ( pfx->exception, GetMagickModule(),
                 OptionError, "undefined shift", "%g", (double) regB);
@@ -3665,7 +3735,7 @@ static MagickBooleanType ExecuteRPN (FxInfo * pfx, fxRtT * pfxrt, fxFltType *res
           regA = log ((double) regA);
           break;
         case fLogtwo:
-          regA = log10((double) regA) / log10(2.0);
+          regA = log2 ((double) regA);
           break;
         case fLog:
           regA = log10 ((double) regA);
@@ -3680,7 +3750,7 @@ static MagickBooleanType ExecuteRPN (FxInfo * pfx, fxRtT * pfxrt, fxFltType *res
           regA = (regA < regB) ? regA : regB;
           break;
         case fMod:
-          if (regB == 0) {
+          if (fabs((double) regB) < MagickEpsilon) {
             regA = 0;
           } else {
             regA = regA - floor((double) (regA/regB))*regB;
@@ -3693,9 +3763,6 @@ static MagickBooleanType ExecuteRPN (FxInfo * pfx, fxRtT * pfxrt, fxFltType *res
           regA = pow ((double) regA, (double) regB);
           break;
         case fRand: {
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
-          #pragma omp critical (MagickCore_ExecuteRPN)
-#endif
           regA = GetPseudoRandomValue (pfxrt->random_info);
           break;
         }
@@ -3706,7 +3773,10 @@ static MagickBooleanType ExecuteRPN (FxInfo * pfx, fxRtT * pfxrt, fxFltType *res
           regA = (regA < 0) ? -1.0 : 1.0;
           break;
         case fSinc:
-          regA = sin ((double) (MagickPI*regA)) / (MagickPI*regA);
+          if (fabs((double) regA) < MagickEpsilon)
+            regA = 1.0;
+          else
+            regA = sin ((double) (MagickPI*regA)) / (MagickPI*regA);
           break;
         case fSinh:
           regA = sinh ((double) regA);
@@ -3993,26 +4063,26 @@ static MagickBooleanType ExecuteRPN (FxInfo * pfx, fxRtT * pfxrt, fxFltType *res
           regA = (fxFltType) GetImageDepth (img, pfx->exception);
           break;
         case aExtent:
-          regA = (fxFltType) img->extent;
+          regA = (fxFltType) GetBlobSize (img);
           break;
         case aKurtosis:
-          if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
+          if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
             regA = cs[WHICH_ATTR_CHAN].kurtosis;
           break;
         case aMaxima:
-          if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
+          if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
             regA = cs[WHICH_ATTR_CHAN].maxima;
           break;
         case aMean:
-          if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
+          if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
             regA = cs[WHICH_ATTR_CHAN].mean;
           break;
         case aMedian:
-          if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
+          if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
             regA = cs[WHICH_ATTR_CHAN].median;
           break;
         case aMinima:
-          if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
+          if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
             regA = cs[WHICH_ATTR_CHAN].minima;
           break;
         case aPage:
@@ -4049,11 +4119,11 @@ static MagickBooleanType ExecuteRPN (FxInfo * pfx, fxRtT * pfxrt, fxFltType *res
           regA = (fxFltType) img->resolution.y;
           break;
         case aSkewness:
-          if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
+          if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
             regA = cs[WHICH_ATTR_CHAN].skewness;
           break;
         case aStdDev:
-          if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
+          if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
             regA = cs[WHICH_ATTR_CHAN].standard_deviation;
           break;
         case aH: /* image->rows */
@@ -4209,6 +4279,7 @@ MagickPrivate MagickBooleanType FxEvaluateChannelExpression (
   assert (pfx->Images != NULL);
   assert (pfx->Imgs != NULL);
   assert (pfx->fxrts != NULL);
+  assert (id >= 0 && id < (int) GetMagickResourceLimit(ThreadResource));

   pfx->fxrts[id].thisPixel = NULL;

@@ -4490,6 +4561,8 @@ MagickExport Image *FxImage(const Image *image,const char *expression,

         q[i] = ClampToQuantum ((MagickRealType) (QuantumRange*result));
       }
+      if (status == MagickFalse)
+        break;
       p+=(ptrdiff_t) GetPixelChannels (image);
       q+=(ptrdiff_t) GetPixelChannels (fx_image);
     }