Commit a003c02ab for imagemagick.org

commit a003c02abc42ac449dd4c8e5df6e91eb1218deec
Author: Cristy <urban-warrior@imagemagick.org>
Date:   Fri Oct 9 20:01:05 2026 -0400

    account for shell escape

diff --git a/MagickCore/string.c b/MagickCore/string.c
index c50741ffa..50d0618d0 100644
--- a/MagickCore/string.c
+++ b/MagickCore/string.c
@@ -2044,12 +2044,7 @@ MagickExport char **StringToArgv(const char *text,int *argc)
     **argv;

   const char
-    *p,
-    *q,
-    *start;
-
-  size_t
-    length;
+    *p = text;

   ssize_t
     i;
@@ -2060,7 +2055,6 @@ MagickExport char **StringToArgv(const char *text,int *argc)
   *argc=0;
   if (text == (const char *) NULL)
     return((char **) NULL);
-  p=text;
   while (*p != '\0')
   {
     /*
@@ -2096,15 +2090,31 @@ MagickExport char **StringToArgv(const char *text,int *argc)
            (isspace((int) ((unsigned char) *p)) == 0) &&
            (*p != '`') && (*p != ';') && (*p != '&') && (*p != '|'))
     {
+      if (*p == '\\')
+        {
+          p++;  /* Skip the escape character */
+          if (*p != '\0')
+            p++;  /* Skip the escaped character */
+          continue;
+        }
       if ((*p == '"') || (*p == '\''))
         {
           const char quote = (*p++);
           while ((*p != quote) && (*p != '\0'))
+          {
+            if ((quote == '"') && (*p == '\\'))
+              {
+                p++;
+                if (*p != '\0')
+                  p++;
+                continue;
+              }
             p++;
-          if (*p == quote)
-            p++;
-          continue;
-        }
+          }
+        if (*p == quote)
+          p++;
+        continue;
+      }
       p++;
     }
   }
@@ -2114,8 +2124,7 @@ MagickExport char **StringToArgv(const char *text,int *argc)
   (*argc)++;
   argv=(char **) AcquireQuantumMemory((size_t) *argc+1UL,sizeof(*argv));
   if (argv == (char **) NULL)
-    ThrowFatalException(ResourceLimitFatalError,
-      "UnableToConvertStringToARGV");
+    ThrowFatalException(ResourceLimitFatalError,"UnableToConvertStringToARGV");
   /*
     Convert the string to an ASCII argument list.
   */
@@ -2123,6 +2132,12 @@ MagickExport char **StringToArgv(const char *text,int *argc)
   p=text;
   for (i=1; i < (ssize_t) *argc; i++)
   {
+    const char
+      *q;
+
+    size_t
+      length = 0;
+
     /*
       Skip whitespace and backtick delimiters.
     */
@@ -2141,14 +2156,29 @@ MagickExport char **StringToArgv(const char *text,int *argc)
           {
             while ((*q != '\0') &&
                    (isspace((int) ((unsigned char) *q)) == 0) &&
-                   (*q != '`') && (*q != ';') && (*q != '&') &&
-                   (*q != '|'))
+                   (*q != '`') && (*q != ';') && (*q != '&') && (*q != '|'))
             {
+              if (*q == '\\')
+                {
+                  q++;
+                  if (*q != '\0')
+                    q++;
+                  continue;
+                }
               if ((*q == '"') || (*q == '\''))
                 {
                   const char quote = (*q++);
                   while ((*q != quote) && (*q != '\0'))
+                  {
+                    if ((quote == '"') && (*q == '\\'))
+                      {
+                        q++;
+                        if (*q != '\0')
+                          q++;
+                        continue;
+                      }
                     q++;
+                  }
                   if (*q == quote)
                     q++;
                   continue;
@@ -2156,8 +2186,11 @@ MagickExport char **StringToArgv(const char *text,int *argc)
               q++;
             }
           }
-    argv[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+
-      MagickPathExtent,sizeof(**argv));
+    /*
+      Allocate buffer matching at least the raw substring size + safety padding.
+    */
+    argv[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MagickPathExtent,
+      sizeof(**argv));
     if (argv[i] == (char *) NULL)
       {
         for (i--; i >= 0; i--)
@@ -2166,20 +2199,47 @@ MagickExport char **StringToArgv(const char *text,int *argc)
         ThrowFatalException(ResourceLimitFatalError,
           "UnableToConvertStringToARGV");
       }
-    start=p;
-    length=(size_t) (q-p);
     /*
-      Remove matching outer single or double quotes.
+      Copy data while unescaping and stripping quotes incrementally.
     */
-    if ((length >= 2) && ((*start == '"') || (*start == '\'')) &&
-        (*(q-1) == *start))
+    if (((p != q) && ((*p == ';') || (*p == '&') || (*p == '|'))))
       {
-        start++;
-        length-=2;
+        while (p < q)
+          argv[i][length++]=(*p++);
+      }
+    else
+      {
+        while (p < q)
+        {
+          if (*p == '\\')
+            {
+              p++;  /* Skip '\\' */
+              if (p < q)
+                argv[i][length++]=(*p++);
+              continue;
+            }
+          if ((*p == '"') || (*p == '\''))
+            {
+              const char quote = *p++; /* Skip open quote */
+              while ((p < q) && (*p != quote))
+              {
+                if ((quote == '"') && (*p == '\\'))
+                  {
+                    p++;  /* Skip inner '\\' */
+                    if (p < q)
+                      argv[i][length++]=(*p++);
+                    continue;
+                  }
+                argv[i][length++]=(*p++);
+              }
+            if ((p < q) && (*p == quote))
+              p++;  /* Skip close quote */
+            continue;
+          }
+          argv[i][length++]=(*p++);
+        }
       }
-    (void) memcpy(argv[i],start,length);
     argv[i][length]='\0';
-    p=q;
   }
   argv[i]=(char *) NULL;
   return(argv);