[PATCH] fix command line argument escaping in build_command_line

Jacob Lifshay programmerjake at gmail.com
Mon Jan 30 21:37:22 CST 2017


fixes bug 40952
---
 dlls/kernel32/process.c | 184 +++++++++++++++++++++++++++---------------------
 1 file changed, 104 insertions(+), 80 deletions(-)

diff --git a/dlls/kernel32/process.c b/dlls/kernel32/process.c
index 2130240..060247c 100644
--- a/dlls/kernel32/process.c
+++ b/dlls/kernel32/process.c
@@ -745,6 +745,10 @@ static void update_library_argv0( const WCHAR *argv0 )
  *   resulting in an odd number of '\' followed by a '"'
  *   '\"'    -> '\\\"'
  *   '\\"'   -> '\\\\\"'
+ * - '\'s are followed by the closing '"' must be doubled,
+ *   resulting in an even number of '\' followed by a '"'
+ *   ' \'    -> '" \\"'
+ *   ' \\'    -> '" \\\\"'
  * - '\'s that are not followed by a '"' can be left as is
  *   'a\b'   == 'a\b'
  *   'a\\b'  == 'a\\b'
@@ -759,36 +763,46 @@ static BOOL build_command_line( WCHAR **argv )
     if (rupp->CommandLine.Buffer) return TRUE; /* already got it from the server */
 
     len = 0;
-    for (arg = argv; *arg; arg++)
-    {
-        BOOL has_space;
-        int bcount;
-        WCHAR* a;
-
-        has_space=FALSE;
-        bcount=0;
-        a=*arg;
-        if( !*a ) has_space=TRUE;
-        while (*a!='\0') {
-            if (*a=='\\') {
-                bcount++;
-            } else {
-                if (*a==' ' || *a=='\t') {
-                    has_space=TRUE;
-                } else if (*a=='"') {
-                    /* doubling of '\' preceding a '"',
-                     * plus escaping of said '"'
-                     */
-                    len+=2*bcount+1;
-                }
-                bcount=0;
+    for(arg = argv; *arg; arg++)
+    {
+        BOOL found_space = FALSE;
+        int slash_count = 0;
+        WCHAR *current_char = *arg;
+        if(arg != argv)
+            len++; /* seperating space */
+        if(!*current_char)
+        {
+            len += 2; /* two quotes */
+            continue;
+        }
+        for(; *current_char; current_char++)
+        {
+            if(*current_char == '\\')
+            {
+                slash_count++;
+                continue;
             }
-            a++;
+            else if(*current_char == '"')
+            {
+                len += 2 + 2 * slash_count; /* doubling of '\' followed by '\"' */
+                slash_count = 0;
+                continue;
+            }
+            else
+            {
+                len += slash_count;
+                slash_count = 0;
+            }
+            if(*current_char == ' ')
+                found_space = TRUE;
+            len++;
         }
-        len+=(a-*arg)+1 /* for the separating space */;
-        if (has_space)
-            len+=2; /* for the quotes */
+        if(found_space)
+            len += 2 + 2 * slash_count; /* doubling of '\' followed by '"' and initial '"' */
+        else
+            len += slash_count;
     }
+    len++; /* terminating null */
 
     if (!(rupp->CommandLine.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR))))
         return FALSE;
@@ -796,65 +810,75 @@ static BOOL build_command_line( WCHAR **argv )
     p = rupp->CommandLine.Buffer;
     rupp->CommandLine.Length = (len - 1) * sizeof(WCHAR);
     rupp->CommandLine.MaximumLength = len * sizeof(WCHAR);
-    for (arg = argv; *arg; arg++)
-    {
-        BOOL has_space,has_quote;
-        WCHAR* a;
-
-        /* Check for quotes and spaces in this argument */
-        has_space=has_quote=FALSE;
-        a=*arg;
-        if( !*a ) has_space=TRUE;
-        while (*a!='\0') {
-            if (*a==' ' || *a=='\t') {
-                has_space=TRUE;
-                if (has_quote)
-                    break;
-            } else if (*a=='"') {
-                has_quote=TRUE;
-                if (has_space)
-                    break;
+    for(arg = argv; *arg; arg++)
+    {
+        BOOL found_space = FALSE;
+        int slash_count = 0;
+        WCHAR *current_char = *arg;
+        if(arg != argv)
+            *p++ = ' '; /* seperating space */
+        if(!*current_char) 
+        {
+            *p++ = '"'; /* two quotes */
+            *p++ = '"';
+            continue;
+        }
+        for(; *current_char; current_char++)
+        {
+            if(*current_char == ' ')
+            {
+                found_space = TRUE;
+                break;
             }
-            a++;
         }
-
-        /* Now transfer it to the command line */
-        if (has_space)
-            *p++='"';
-        if (has_quote) {
-            int bcount;
-
-            bcount=0;
-            a=*arg;
-            while (*a!='\0') {
-                if (*a=='\\') {
-                    *p++=*a;
-                    bcount++;
-                } else {
-                    if (*a=='"') {
-                        int i;
-
-                        /* Double all the '\\' preceding this '"', plus one */
-                        for (i=0;i<=bcount;i++)
-                            *p++='\\';
-                        *p++='"';
-                    } else {
-                        *p++=*a;
-                    }
-                    bcount=0;
+        if(found_space)
+            *p++ = '"';
+        for(current_char = *arg; *current_char; current_char++)
+        {
+            if(*current_char == '\\')
+            {
+                slash_count++;
+                continue;
+            }
+            else if(*current_char == '"')
+            {
+                /* doubling of '\' followed by '\"' */
+                while(slash_count-- > 0)
+                {
+                    *p++ = '\\';
+                    *p++ = '\\';
                 }
-                a++;
+                *p++ = '\\';
+                *p++ = '"';
+                slash_count = 0;
+                continue;
             }
-        } else {
-            WCHAR* x = *arg;
-            while ((*p=*x++)) p++;
+            else
+            {
+                while(slash_count-- > 0)
+                    *p++ = '\\';
+                slash_count = 0;
+            }
+            if(*current_char == ' ')
+                found_space = TRUE;
+            *p++ = *current_char;
+        }
+        if(found_space)
+        {
+            /* doubling of '\' followed by '"' and initial '"' */
+            while(slash_count-- > 0)
+            {
+                *p++ = '\\';
+                *p++ = '\\';
+            }
+            *p++ = '"';
+        }
+        else
+        {
+            while(slash_count-- > 0)
+                *p++ = '\\';
         }
-        if (has_space)
-            *p++='"';
-        *p++=' ';
     }
-    if (p > rupp->CommandLine.Buffer)
-        p--;  /* remove last space */
     *p = '\0';
 
     return TRUE;
-- 
2.7.4




More information about the wine-patches mailing list