[PATCH 3/3] [cmd] Avoid infinite loop running batch program

Jason Edmeades jason at edmeades.me.uk
Tue Jun 4 16:08:38 CDT 2013


Attempting to run a batch program called test(123).bat will generate an
infinite loop due to some old, and now redundant code in cmd. Such an
attempt should fail (as the bracket is the command delimiter) as it does on
windows.

The root cause of the infinite loop is a patch we needed back in 2007 (added by
me... http://www.winehq.org/pipermail/wine-patches/2007-April/037866.html )
because internal exe's did not have entries in the wine tree, hence could not
be located (in that case, xcopy). This has long since been fixed so I believe
all the 'assumeIntenal' code in wcmdmain is redundant. However in this case,
wine 'cleverly' now parses the filename into test, locates it and cannot find
it, then has one last attempt by spawning "cmd.exe /c test(123).bat", getting
into its loop.

[Fixes bug 33189]
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-patches/attachments/20130604/9e9d8e1d/attachment.html>
-------------- next part --------------
From 9e17939269422166ec50aedeb89caf3de8f9fbc6 Mon Sep 17 00:00:00 2001
From: Jason Edmeades <jason at edmeades.me.uk>
Date: Tue, 4 Jun 2013 21:27:48 +0100
Subject: [PATCH 3/3] [cmd] Avoid infinite loop running batch program

Attempting to run a batch program called test(123).bat will generate an
infinite loop due to some old, and now redundant code in cmd. Such an
attempt should fail (as the bracket is the command delimiter) as it does on
windows.

The root cause of the infinite loop is a patch we needed back in 2007 (added by
me... http://www.winehq.org/pipermail/wine-patches/2007-April/037866.html )
because internal exe's did not have entries in the wine tree, hence could not
be located (in that case, xcopy). This has long since been fixed so I believe
all the 'assumeIntenal' code in wcmdmain is redundant. However in this case,
wine 'cleverly' now parses the filename into test, locates it and cannot find
it, then has one last attempt by spawning "cmd.exe /c test(123).bat", getting
into its loop.

[Fixes bug 33189]
---
 programs/cmd/tests/test_cmdline.cmd     |    8 +++++++-
 programs/cmd/tests/test_cmdline.cmd.exp |    1 +
 programs/cmd/wcmdmain.c                 |   21 +++++----------------
 3 files changed, 13 insertions(+), 17 deletions(-)

diff --git a/programs/cmd/tests/test_cmdline.cmd b/programs/cmd/tests/test_cmdline.cmd
index e563341..f73333f 100644
--- a/programs/cmd/tests/test_cmdline.cmd
+++ b/programs/cmd/tests/test_cmdline.cmd
@@ -95,6 +95,7 @@ echo @echo 1 > "say one.bat"
 echo @echo 2 > "saytwo.bat"
 echo @echo 3 > "say (3).bat"
 echo @echo 4 > "say .bat"
+echo @echo 5 > "bazbaz(5).bat"
 
 echo ------ Testing invocation of batch files ----------
 call say one
@@ -144,6 +145,11 @@ if errorlevel 2 echo error %ErrorLevel%
 call :setError 0
 cmd /c say" "(3) prints 4?!
 if errorlevel 2 echo error %ErrorLevel%
+call :setError 0
+rem Deliberately invoking a fully qualified batch name containing a bracket
+rem should fail, as a bracket is a command delimiter.
+cmd /c "bazbaz(5).bat"
+if errorlevel 1 echo Passed
 
 echo ---------- Testing CMD /C quoting -----------------
 cmd /c @echo "hi"
@@ -261,7 +267,7 @@ call tell(1234)
 call tell(12(34)
 call tell(12;34)
 echo --------- Finished  --------------
-del tell.bat say*.*
+del tell.bat say*.* bazbaz.bat
 exit
 :setError
 exit /B %1
diff --git a/programs/cmd/tests/test_cmdline.cmd.exp b/programs/cmd/tests/test_cmdline.cmd.exp
index 38e17a5..980f674 100644
--- a/programs/cmd/tests/test_cmdline.cmd.exp
+++ b/programs/cmd/tests/test_cmdline.cmd.exp
@@ -70,6 +70,7 @@ var=33 at space@
 0 at space@
 3 at space@
 4 at space@
+Passed
 ---------- Testing CMD /C quoting -----------------
 "hi"
 1 at space@
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index 0d4575b..a211efa 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -1048,7 +1048,6 @@ void WCMD_run_program (WCHAR *command, BOOL called)
   BOOL  extensionsupplied = FALSE;
   BOOL  launched = FALSE;
   BOOL  status;
-  BOOL  assumeInternal = FALSE;
   DWORD len;
   static const WCHAR envPath[] = {'P','A','T','H','\0'};
   static const WCHAR delims[] = {'/','\\',':','\0'};
@@ -1168,20 +1167,8 @@ void WCMD_run_program (WCHAR *command, BOOL called)
       }
     }
 
-   /* Internal programs won't be picked up by this search, so even
-      though not found, try one last createprocess and wait for it
-      to complete.
-      Note: Ideally we could tell between a console app (wait) and a
-      windows app, but the API's for it fail in this case           */
-    if (!found && pathposn == NULL) {
-        WINE_TRACE("ASSUMING INTERNAL\n");
-        assumeInternal = TRUE;
-    } else {
-        WINE_TRACE("Found as %s\n", wine_dbgstr_w(thisDir));
-    }
-
     /* Once found, launch it */
-    if (found || assumeInternal) {
+    if (found) {
       STARTUPINFOW st;
       PROCESS_INFORMATION pe;
       SHFILEINFOW psfi;
@@ -1191,6 +1178,8 @@ void WCMD_run_program (WCHAR *command, BOOL called)
       static const WCHAR batExt[] = {'.','b','a','t','\0'};
       static const WCHAR cmdExt[] = {'.','c','m','d','\0'};
 
+      WINE_TRACE("Found as %s\n", wine_dbgstr_w(thisDir));
+
       /* Special case BAT and CMD */
       if (ext && (!strcmpiW(ext, batExt) || !strcmpiW(ext, cmdExt))) {
         BOOL oldinteractive = interactive;
@@ -1214,7 +1203,7 @@ void WCMD_run_program (WCHAR *command, BOOL called)
 
         /* Launch the process and if a CUI wait on it to complete
            Note: Launching internal wine processes cannot specify a full path to exe */
-        status = CreateProcessW(assumeInternal?NULL : thisDir,
+        status = CreateProcessW(thisDir,
                                 command, NULL, NULL, TRUE, 0, NULL, NULL, &st, &pe);
         heap_free(st.lpReserved2);
         if ((opt_c || opt_k) && !opt_s && !status
@@ -1231,7 +1220,7 @@ void WCMD_run_program (WCHAR *command, BOOL called)
 
         /* Always wait when non-interactive (cmd /c or in batch program),
            or for console applications                                    */
-        if (assumeInternal || !interactive || (console && !HIWORD(console)))
+        if (!interactive || (console && !HIWORD(console)))
             WaitForSingleObject (pe.hProcess, INFINITE);
         GetExitCodeProcess (pe.hProcess, &errorlevel);
         if (errorlevel == STILL_ACTIVE) errorlevel = 0;
-- 
1.7.9.5


More information about the wine-patches mailing list