Skip to content

Commit c7fdc9c

Browse files
zoobaarhadthedevambv
authored
[3.7] gh-101283: Improved fallback logic for subprocess with shell=True on Windows (GH-101286) (#101713)
Co-authored-by: Oleg Iarygin <[email protected]> Co-authored-by: Łukasz Langa <[email protected]> Co-authored-by: Oleg Iarygin <[email protected]>
1 parent ecfed4f commit c7fdc9c

File tree

3 files changed

+63
-1
lines changed

3 files changed

+63
-1
lines changed

Doc/library/subprocess.rst

+43
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.7.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.
@@ -442,6 +450,17 @@ functions.
442450
:program:`ps`. If ``shell=True``, on POSIX the *executable* argument
443451
specifies a replacement shell for the default :file:`/bin/sh`.
444452

453+
.. versionchanged:: 3.6
454+
*executable* parameter accepts a :term:`path-like object` on POSIX.
455+
456+
.. versionchanged:: 3.7.17
457+
458+
Changed Windows shell search order for ``shell=True``. The current
459+
directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and
460+
``%SystemRoot%\System32\cmd.exe``. As a result, dropping a
461+
malicious program named ``cmd.exe`` into a current directory no
462+
longer works.
463+
445464
*stdin*, *stdout* and *stderr* specify the executed program's standard input,
446465
standard output and standard error file handles, respectively. Valid values
447466
are :data:`PIPE`, :data:`DEVNULL`, an existing file descriptor (a positive
@@ -1032,6 +1051,14 @@ calls these functions.
10321051
.. versionchanged:: 3.3
10331052
*timeout* was added.
10341053

1054+
.. versionchanged:: 3.7.17
1055+
1056+
Changed Windows shell search order for ``shell=True``. The current
1057+
directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and
1058+
``%SystemRoot%\System32\cmd.exe``. As a result, dropping a
1059+
malicious program named ``cmd.exe`` into a current directory no
1060+
longer works.
1061+
10351062
.. function:: check_call(args, *, stdin=None, stdout=None, stderr=None, \
10361063
shell=False, cwd=None, timeout=None, \
10371064
**other_popen_kwargs)
@@ -1062,6 +1089,14 @@ calls these functions.
10621089
.. versionchanged:: 3.3
10631090
*timeout* was added.
10641091

1092+
.. versionchanged:: 3.7.17
1093+
1094+
Changed Windows shell search order for ``shell=True``. The current
1095+
directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and
1096+
``%SystemRoot%\System32\cmd.exe``. As a result, dropping a
1097+
malicious program named ``cmd.exe`` into a current directory no
1098+
longer works.
1099+
10651100

10661101
.. function:: check_output(args, *, stdin=None, stderr=None, shell=False, \
10671102
cwd=None, encoding=None, errors=None, \
@@ -1116,6 +1151,14 @@ calls these functions.
11161151
.. versionadded:: 3.7
11171152
*text* was added as a more readable alias for *universal_newlines*.
11181153

1154+
.. versionchanged:: 3.7.17
1155+
1156+
Changed Windows shell search order for ``shell=True``. The current
1157+
directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and
1158+
``%SystemRoot%\System32\cmd.exe``. As a result, dropping a
1159+
malicious program named ``cmd.exe`` into a current directory no
1160+
longer works.
1161+
11191162

11201163
.. _subprocess-replacements:
11211164

Lib/subprocess.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -1192,7 +1192,23 @@ def _execute_child(self, args, executable, preexec_fn, close_fds,
11921192
if shell:
11931193
startupinfo.dwFlags |= _winapi.STARTF_USESHOWWINDOW
11941194
startupinfo.wShowWindow = _winapi.SW_HIDE
1195-
comspec = os.environ.get("COMSPEC", "cmd.exe")
1195+
if not executable:
1196+
# gh-101283: without a fully-qualified path, before Windows
1197+
# checks the system directories, it first looks in the
1198+
# application directory, and also the current directory if
1199+
# NeedCurrentDirectoryForExePathW(ExeName) is true, so try
1200+
# to avoid executing unqualified "cmd.exe".
1201+
comspec = os.environ.get('ComSpec')
1202+
if not comspec:
1203+
system_root = os.environ.get('SystemRoot', '')
1204+
comspec = os.path.join(system_root, 'System32', 'cmd.exe')
1205+
if not os.path.isabs(comspec):
1206+
raise FileNotFoundError('shell not found: neither %ComSpec% nor %SystemRoot% is set')
1207+
if os.path.isabs(comspec):
1208+
executable = comspec
1209+
else:
1210+
comspec = executable
1211+
11961212
args = '{} /c "{}"'.format (comspec, args)
11971213

11981214
# Start the process
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)