Skip to content

Commit 32a1a61

Browse files
miss-islingtonarhadthedevzooba
authored
[3.8] gh-101283: Improved fallback logic for subprocess with shell=True on Windows (GH-101286) (#101710)
Co-authored-by: Oleg Iarygin <[email protected]> Co-authored-by: Steve Dower <[email protected]>
1 parent 41d301a commit 32a1a61

File tree

3 files changed

+60
-1
lines changed

3 files changed

+60
-1
lines changed

Doc/library/subprocess.rst

+40
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ compatibility with older versions, see the :ref:`call-function-trio` section.
111111
Added the *text* parameter, as a more understandable alias of *universal_newlines*.
112112
Added the *capture_output* parameter.
113113

114+
.. versionchanged:: 3.8.17
115+
116+
Changed Windows shell search order for ``shell=True``. The current
117+
directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and
118+
``%SystemRoot%\System32\cmd.exe``. As a result, dropping a
119+
malicious program named ``cmd.exe`` into a current directory no
120+
longer works.
121+
114122
.. class:: CompletedProcess
115123

116124
The return value from :func:`run`, representing a process that has finished.
@@ -459,6 +467,14 @@ functions.
459467
*executable* parameter accepts a bytes and :term:`path-like object`
460468
on Windows.
461469

470+
.. versionchanged:: 3.8.17
471+
472+
Changed Windows shell search order for ``shell=True``. The current
473+
directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and
474+
``%SystemRoot%\System32\cmd.exe``. As a result, dropping a
475+
malicious program named ``cmd.exe`` into a current directory no
476+
longer works.
477+
462478
*stdin*, *stdout* and *stderr* specify the executed program's standard input,
463479
standard output and standard error file handles, respectively. Valid values
464480
are :data:`PIPE`, :data:`DEVNULL`, an existing file descriptor (a positive
@@ -1077,6 +1093,14 @@ calls these functions.
10771093
.. versionchanged:: 3.3
10781094
*timeout* was added.
10791095

1096+
.. versionchanged:: 3.8.17
1097+
1098+
Changed Windows shell search order for ``shell=True``. The current
1099+
directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and
1100+
``%SystemRoot%\System32\cmd.exe``. As a result, dropping a
1101+
malicious program named ``cmd.exe`` into a current directory no
1102+
longer works.
1103+
10801104
.. function:: check_call(args, *, stdin=None, stdout=None, stderr=None, \
10811105
shell=False, cwd=None, timeout=None, \
10821106
**other_popen_kwargs)
@@ -1107,6 +1131,14 @@ calls these functions.
11071131
.. versionchanged:: 3.3
11081132
*timeout* was added.
11091133

1134+
.. versionchanged:: 3.8.17
1135+
1136+
Changed Windows shell search order for ``shell=True``. The current
1137+
directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and
1138+
``%SystemRoot%\System32\cmd.exe``. As a result, dropping a
1139+
malicious program named ``cmd.exe`` into a current directory no
1140+
longer works.
1141+
11101142

11111143
.. function:: check_output(args, *, stdin=None, stderr=None, shell=False, \
11121144
cwd=None, encoding=None, errors=None, \
@@ -1162,6 +1194,14 @@ calls these functions.
11621194
.. versionadded:: 3.7
11631195
*text* was added as a more readable alias for *universal_newlines*.
11641196

1197+
.. versionchanged:: 3.8.17
1198+
1199+
Changed Windows shell search order for ``shell=True``. The current
1200+
directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and
1201+
``%SystemRoot%\System32\cmd.exe``. As a result, dropping a
1202+
malicious program named ``cmd.exe`` into a current directory no
1203+
longer works.
1204+
11651205

11661206
.. _subprocess-replacements:
11671207

Lib/subprocess.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -1298,7 +1298,23 @@ def _execute_child(self, args, executable, preexec_fn, close_fds,
12981298
if shell:
12991299
startupinfo.dwFlags |= _winapi.STARTF_USESHOWWINDOW
13001300
startupinfo.wShowWindow = _winapi.SW_HIDE
1301-
comspec = os.environ.get("COMSPEC", "cmd.exe")
1301+
if not executable:
1302+
# gh-101283: without a fully-qualified path, before Windows
1303+
# checks the system directories, it first looks in the
1304+
# application directory, and also the current directory if
1305+
# NeedCurrentDirectoryForExePathW(ExeName) is true, so try
1306+
# to avoid executing unqualified "cmd.exe".
1307+
comspec = os.environ.get('ComSpec')
1308+
if not comspec:
1309+
system_root = os.environ.get('SystemRoot', '')
1310+
comspec = os.path.join(system_root, 'System32', 'cmd.exe')
1311+
if not os.path.isabs(comspec):
1312+
raise FileNotFoundError('shell not found: neither %ComSpec% nor %SystemRoot% is set')
1313+
if os.path.isabs(comspec):
1314+
executable = comspec
1315+
else:
1316+
comspec = executable
1317+
13021318
args = '{} /c "{}"'.format (comspec, args)
13031319

13041320
if cwd is not None:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:class:`subprocess.Popen` now uses a safer approach to find
2+
``cmd.exe`` when launching with ``shell=True``. Patch by Eryk Sun,
3+
based on a patch by Oleg Iarygin.

0 commit comments

Comments
 (0)