python / cpython

The Python programming language
https://www.python.org
Other
62.29k stars 29.93k forks source link

subprocess should quote `comspec` when forming its `args` string #101718

Open zooba opened 1 year ago

zooba commented 1 year ago

The value of args should quote comspec. Otherwise the shell process won't parse its command line correctly if comspec contains spaces.

_Originally posted by @eryksun in https://github.com/python/cpython/pull/101712#discussion_r1100761091_

amiremohamadi commented 1 year ago

could you provide an example to reproduce this scenario please? I think winapi handle it perfectly.

eryksun commented 1 year ago

Regarding the API, previously with shell=True we didn't use lpApplicationName (i.e. the executable argument of subprocess.Popen). In this case the API has to parse the command out of the command line and search for the application name to use. For example, if the command line is r'C:\Program Files\JPSoft\TCCLE14x64\tcc.exe /c "dir"', then CreateProcessW() first looks for "C:\Program". It checks for the given name and also "C:\Program.exe". If that's not found, it consumes up to the next space character and looks for "C:\Program Files\JPSoft\TCCLE14x64\tcc.exe". If that's an existing file, it's used as the application name to execute.

In a related PR, subprocess.Popen has been modified to use lpApplicationName if the value of the "ComSpec" environment variable is an absolute path. In this case, the API doesn't have to search for the executable. Still the shell itself still has to parse its command line into command-line arguments, which may fail if the command isn't quoted. A shell is probably resilient to this, but that doesn't excuse the mistake in subprocess.Popen.

For an artificial example, let's run Python itself without quoting the executable path in the command line:

>>> os.getcwd()
'C:\\'
>>> sys.executable
'C:\\Program Files\\Python311\\python.exe'
>>> subprocess.call(fr'{sys.executable} -c "print(42)"', executable=sys.executable)
C:\Program: can't open file 'C:\\Files\\Python311\\python.exe': [Errno 2] No such file or directory
2

Python (rather, the C runtime library) parsed the command line as the argument list ['C:\\Program", "Files\\Python311\\python.exe", "-c", "print(42)"]. Let's fix this:

>>> subprocess.call(fr'"{sys.executable}" -c "print(42)"', executable=sys.executable)
42
0