Skip to content

Commit ef2d52f

Browse files
Sync with main
2 parents 9f68cd1 + 244d4cd commit ef2d52f

File tree

10 files changed

+163
-270
lines changed

10 files changed

+163
-270
lines changed

Doc/library/subprocess.rst

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ underlying :class:`Popen` interface can be used directly.
111111
Added the *text* parameter, as a more understandable alias of *universal_newlines*.
112112
Added the *capture_output* parameter.
113113

114+
.. versionchanged:: 3.11.3
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.
@@ -487,6 +495,14 @@ functions.
487495
*executable* parameter accepts a bytes and :term:`path-like object`
488496
on Windows.
489497

498+
.. versionchanged:: 3.11.3
499+
500+
Changed Windows shell search order for ``shell=True``. The current
501+
directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and
502+
``%SystemRoot%\System32\cmd.exe``. As a result, dropping a
503+
malicious program named ``cmd.exe`` into a current directory no
504+
longer works.
505+
490506
*stdin*, *stdout* and *stderr* specify the executed program's standard input,
491507
standard output and standard error file handles, respectively. Valid values
492508
are ``None``, :data:`PIPE`, :data:`DEVNULL`, an existing file descriptor (a
@@ -1158,6 +1174,14 @@ calls these functions.
11581174
.. versionchanged:: 3.3
11591175
*timeout* was added.
11601176

1177+
.. versionchanged:: 3.11.3
1178+
1179+
Changed Windows shell search order for ``shell=True``. The current
1180+
directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and
1181+
``%SystemRoot%\System32\cmd.exe``. As a result, dropping a
1182+
malicious program named ``cmd.exe`` into a current directory no
1183+
longer works.
1184+
11611185
.. function:: check_call(args, *, stdin=None, stdout=None, stderr=None, \
11621186
shell=False, cwd=None, timeout=None, \
11631187
**other_popen_kwargs)
@@ -1190,6 +1214,14 @@ calls these functions.
11901214
.. versionchanged:: 3.3
11911215
*timeout* was added.
11921216

1217+
.. versionchanged:: 3.11.3
1218+
1219+
Changed Windows shell search order for ``shell=True``. The current
1220+
directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and
1221+
``%SystemRoot%\System32\cmd.exe``. As a result, dropping a
1222+
malicious program named ``cmd.exe`` into a current directory no
1223+
longer works.
1224+
11931225

11941226
.. function:: check_output(args, *, stdin=None, stderr=None, shell=False, \
11951227
cwd=None, encoding=None, errors=None, \
@@ -1245,6 +1277,14 @@ calls these functions.
12451277
.. versionadded:: 3.7
12461278
*text* was added as a more readable alias for *universal_newlines*.
12471279

1280+
.. versionchanged:: 3.11.3
1281+
1282+
Changed Windows shell search order for ``shell=True``. The current
1283+
directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and
1284+
``%SystemRoot%\System32\cmd.exe``. As a result, dropping a
1285+
malicious program named ``cmd.exe`` into a current directory no
1286+
longer works.
1287+
12481288

12491289
.. _subprocess-replacements:
12501290

Doc/whatsnew/3.12.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,10 @@ Pending Removal in Python 3.14
512512
:func:`~multiprocessing.set_start_method` APIs to explicitly specify when
513513
your code *requires* ``'fork'``. See :ref:`multiprocessing-start-methods`.
514514

515+
* :mod:`pty` has two undocumented ``master_open()`` and ``slave_open()``
516+
functions that have been deprecated since Python 2 but only gained a
517+
proper :exc:`DeprecationWarning` in 3.12. Remove them in 3.14.
518+
515519
Pending Removal in Future Versions
516520
----------------------------------
517521

Lib/pty.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ def master_open():
4040
Open a pty master and return the fd, and the filename of the slave end.
4141
Deprecated, use openpty() instead."""
4242

43+
import warnings
44+
warnings.warn("Use pty.openpty() instead.", DeprecationWarning, stacklevel=2) # Remove API in 3.14
45+
4346
try:
4447
master_fd, slave_fd = os.openpty()
4548
except (AttributeError, OSError):
@@ -69,6 +72,9 @@ def slave_open(tty_name):
6972
opened filedescriptor.
7073
Deprecated, use openpty() instead."""
7174

75+
import warnings
76+
warnings.warn("Use pty.openpty() instead.", DeprecationWarning, stacklevel=2) # Remove API in 3.14
77+
7278
result = os.open(tty_name, os.O_RDWR)
7379
try:
7480
from fcntl import ioctl, I_PUSH
@@ -101,20 +107,8 @@ def fork():
101107
master_fd, slave_fd = openpty()
102108
pid = os.fork()
103109
if pid == CHILD:
104-
# Establish a new session.
105-
os.setsid()
106110
os.close(master_fd)
107-
108-
# Slave becomes stdin/stdout/stderr of child.
109-
os.dup2(slave_fd, STDIN_FILENO)
110-
os.dup2(slave_fd, STDOUT_FILENO)
111-
os.dup2(slave_fd, STDERR_FILENO)
112-
if slave_fd > STDERR_FILENO:
113-
os.close(slave_fd)
114-
115-
# Explicitly open the tty to make it become a controlling tty.
116-
tmp_fd = os.open(os.ttyname(STDOUT_FILENO), os.O_RDWR)
117-
os.close(tmp_fd)
111+
os.login_tty(slave_fd)
118112
else:
119113
os.close(slave_fd)
120114

Lib/subprocess.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1480,7 +1480,23 @@ def _execute_child(self, args, executable, preexec_fn, close_fds,
14801480
if shell:
14811481
startupinfo.dwFlags |= _winapi.STARTF_USESHOWWINDOW
14821482
startupinfo.wShowWindow = _winapi.SW_HIDE
1483-
comspec = os.environ.get("COMSPEC", "cmd.exe")
1483+
if not executable:
1484+
# gh-101283: without a fully-qualified path, before Windows
1485+
# checks the system directories, it first looks in the
1486+
# application directory, and also the current directory if
1487+
# NeedCurrentDirectoryForExePathW(ExeName) is true, so try
1488+
# to avoid executing unqualified "cmd.exe".
1489+
comspec = os.environ.get('ComSpec')
1490+
if not comspec:
1491+
system_root = os.environ.get('SystemRoot', '')
1492+
comspec = os.path.join(system_root, 'System32', 'cmd.exe')
1493+
if not os.path.isabs(comspec):
1494+
raise FileNotFoundError('shell not found: neither %ComSpec% nor %SystemRoot% is set')
1495+
if os.path.isabs(comspec):
1496+
executable = comspec
1497+
else:
1498+
comspec = executable
1499+
14841500
args = '{} /c "{}"'.format (comspec, args)
14851501

14861502
if cwd is not None:

Makefile.pre.in

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,24 +1445,21 @@ regen-opcode-targets:
14451445

14461446
.PHONY: regen-cases
14471447
regen-cases:
1448-
# Regenerate Python/generated_cases.c.h from Python/bytecodes.c
1448+
# Regenerate Python/generated_cases.c.h
1449+
# and Python/opcode_metadata.h
1450+
# from Python/bytecodes.c
14491451
# using Tools/cases_generator/generate_cases.py
14501452
PYTHONPATH=$(srcdir)/Tools/cases_generator \
14511453
$(PYTHON_FOR_REGEN) \
14521454
$(srcdir)/Tools/cases_generator/generate_cases.py \
14531455
-i $(srcdir)/Python/bytecodes.c \
1454-
-o $(srcdir)/Python/generated_cases.c.h.new
1456+
-o $(srcdir)/Python/generated_cases.c.h.new \
1457+
-m $(srcdir)/Python/opcode_metadata.h.new
14551458
$(UPDATE_FILE) $(srcdir)/Python/generated_cases.c.h $(srcdir)/Python/generated_cases.c.h.new
1456-
# Regenerate Python/opcode_metadata.h from Python/bytecodes.c
1457-
# using Tools/cases_generator/generate_cases.py --metadata
1458-
PYTHONPATH=$(srcdir)/Tools/cases_generator \
1459-
$(PYTHON_FOR_REGEN) \
1460-
$(srcdir)/Tools/cases_generator/generate_cases.py \
1461-
--metadata \
1462-
-i $(srcdir)/Python/bytecodes.c \
1463-
-o $(srcdir)/Python/opcode_metadata.h.new
14641459
$(UPDATE_FILE) $(srcdir)/Python/opcode_metadata.h $(srcdir)/Python/opcode_metadata.h.new
14651460

1461+
Python/compile.o: $(srcdir)/Python/opcode_metadata.h
1462+
14661463
Python/ceval.o: \
14671464
$(srcdir)/Python/ceval_macros.h \
14681465
$(srcdir)/Python/condvar.h \
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Refactored the implementation of :func:`pty.fork` to use :func:`os.login_tty`.
2+
3+
A :exc:`DeprecationWarning` is now raised by ``pty.master_open()`` and ``pty.slave_open()``. They were
4+
undocumented and deprecated long long ago in the docstring in favor of :func:`pty.openpty`.
Lines changed: 3 additions & 0 deletions
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)