mirror of https://github.com/python/cpython
gh-121381 Remove subprocess._USE_VFORK escape hatch (#121383)
This flag was added as an escape hatch in gh-91401 and backported to Python 3.10. The flag broke at some point between its addition and now. As there is currently no publicly known environments that require this, remove it rather than work on fixing it. This leaves the flag in the subprocess module to not break code which may have used / checked the flag itself. discussion: https://discuss.python.org/t/subprocess-use-vfork-escape-hatch-broken-fix-or-remove/56915/2
This commit is contained in:
parent
82db572813
commit
a9344cdffa
|
@ -1561,36 +1561,22 @@ runtime):
|
||||||
Module which provides function to parse and escape command lines.
|
Module which provides function to parse and escape command lines.
|
||||||
|
|
||||||
|
|
||||||
.. _disable_vfork:
|
|
||||||
.. _disable_posix_spawn:
|
.. _disable_posix_spawn:
|
||||||
|
|
||||||
Disabling use of ``vfork()`` or ``posix_spawn()``
|
Disable use of ``posix_spawn()``
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
On Linux, :mod:`subprocess` defaults to using the ``vfork()`` system call
|
On Linux, :mod:`subprocess` defaults to using the ``vfork()`` system call
|
||||||
internally when it is safe to do so rather than ``fork()``. This greatly
|
internally when it is safe to do so rather than ``fork()``. This greatly
|
||||||
improves performance.
|
improves performance.
|
||||||
|
|
||||||
If you ever encounter a presumed highly unusual situation where you need to
|
|
||||||
prevent ``vfork()`` from being used by Python, you can set the
|
|
||||||
:const:`subprocess._USE_VFORK` attribute to a false value.
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
subprocess._USE_VFORK = False # See CPython issue gh-NNNNNN.
|
|
||||||
|
|
||||||
Setting this has no impact on use of ``posix_spawn()`` which could use
|
|
||||||
``vfork()`` internally within its libc implementation. There is a similar
|
|
||||||
:const:`subprocess._USE_POSIX_SPAWN` attribute if you need to prevent use of
|
|
||||||
that.
|
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
subprocess._USE_POSIX_SPAWN = False # See CPython issue gh-NNNNNN.
|
subprocess._USE_POSIX_SPAWN = False # See CPython issue gh-NNNNNN.
|
||||||
|
|
||||||
It is safe to set these to false on any Python version. They will have no
|
It is safe to set this to false on any Python version. It will have no
|
||||||
effect on older versions when unsupported. Do not assume the attributes are
|
effect on older or newer versions where unsupported. Do not assume the attribute
|
||||||
available to read. Despite their names, a true value does not indicate that the
|
is available to read. Despite the name, a true value does not indicate the
|
||||||
corresponding function will be used, only that it may be.
|
corresponding function will be used, only that it may be.
|
||||||
|
|
||||||
Please file issues any time you have to use these private knobs with a way to
|
Please file issues any time you have to use these private knobs with a way to
|
||||||
|
@ -1598,4 +1584,3 @@ reproduce the issue you were seeing. Link to that issue from a comment in your
|
||||||
code.
|
code.
|
||||||
|
|
||||||
.. versionadded:: 3.8 ``_USE_POSIX_SPAWN``
|
.. versionadded:: 3.8 ``_USE_POSIX_SPAWN``
|
||||||
.. versionadded:: 3.11 ``_USE_VFORK``
|
|
||||||
|
|
|
@ -445,8 +445,7 @@ def spawnv_passfds(path, args, passfds):
|
||||||
return _posixsubprocess.fork_exec(
|
return _posixsubprocess.fork_exec(
|
||||||
args, [path], True, passfds, None, None,
|
args, [path], True, passfds, None, None,
|
||||||
-1, -1, -1, -1, -1, -1, errpipe_read, errpipe_write,
|
-1, -1, -1, -1, -1, -1, errpipe_read, errpipe_write,
|
||||||
False, False, -1, None, None, None, -1, None,
|
False, False, -1, None, None, None, -1, None)
|
||||||
subprocess._USE_VFORK)
|
|
||||||
finally:
|
finally:
|
||||||
os.close(errpipe_read)
|
os.close(errpipe_read)
|
||||||
os.close(errpipe_write)
|
os.close(errpipe_write)
|
||||||
|
|
|
@ -749,7 +749,6 @@ def _use_posix_spawn():
|
||||||
# These are primarily fail-safe knobs for negatives. A True value does not
|
# These are primarily fail-safe knobs for negatives. A True value does not
|
||||||
# guarantee the given libc/syscall API will be used.
|
# guarantee the given libc/syscall API will be used.
|
||||||
_USE_POSIX_SPAWN = _use_posix_spawn()
|
_USE_POSIX_SPAWN = _use_posix_spawn()
|
||||||
_USE_VFORK = True
|
|
||||||
_HAVE_POSIX_SPAWN_CLOSEFROM = hasattr(os, 'POSIX_SPAWN_CLOSEFROM')
|
_HAVE_POSIX_SPAWN_CLOSEFROM = hasattr(os, 'POSIX_SPAWN_CLOSEFROM')
|
||||||
|
|
||||||
|
|
||||||
|
@ -1902,7 +1901,7 @@ class Popen:
|
||||||
errpipe_read, errpipe_write,
|
errpipe_read, errpipe_write,
|
||||||
restore_signals, start_new_session,
|
restore_signals, start_new_session,
|
||||||
process_group, gid, gids, uid, umask,
|
process_group, gid, gids, uid, umask,
|
||||||
preexec_fn, _USE_VFORK)
|
preexec_fn)
|
||||||
self._child_created = True
|
self._child_created = True
|
||||||
finally:
|
finally:
|
||||||
# be sure the FD is closed no matter what
|
# be sure the FD is closed no matter what
|
||||||
|
|
|
@ -120,7 +120,7 @@ class CAPITest(unittest.TestCase):
|
||||||
return 1
|
return 1
|
||||||
with self.assertRaisesRegex(TypeError, 'indexing'):
|
with self.assertRaisesRegex(TypeError, 'indexing'):
|
||||||
_posixsubprocess.fork_exec(
|
_posixsubprocess.fork_exec(
|
||||||
1,Z(),True,(1, 2),5,6,7,8,9,10,11,12,13,14,True,True,17,False,19,20,21,22,False)
|
1,Z(),True,(1, 2),5,6,7,8,9,10,11,12,13,14,True,True,17,False,19,20,21,22)
|
||||||
# Issue #15736: overflow in _PySequence_BytesToCharpArray()
|
# Issue #15736: overflow in _PySequence_BytesToCharpArray()
|
||||||
class Z(object):
|
class Z(object):
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
|
@ -128,7 +128,7 @@ class CAPITest(unittest.TestCase):
|
||||||
def __getitem__(self, i):
|
def __getitem__(self, i):
|
||||||
return b'x'
|
return b'x'
|
||||||
self.assertRaises(MemoryError, _posixsubprocess.fork_exec,
|
self.assertRaises(MemoryError, _posixsubprocess.fork_exec,
|
||||||
1,Z(),True,(1, 2),5,6,7,8,9,10,11,12,13,14,True,True,17,False,19,20,21,22,False)
|
1,Z(),True,(1, 2),5,6,7,8,9,10,11,12,13,14,True,True,17,False,19,20,21,22)
|
||||||
|
|
||||||
@unittest.skipUnless(_posixsubprocess, '_posixsubprocess required for this test.')
|
@unittest.skipUnless(_posixsubprocess, '_posixsubprocess required for this test.')
|
||||||
def test_subprocess_fork_exec(self):
|
def test_subprocess_fork_exec(self):
|
||||||
|
@ -138,7 +138,7 @@ class CAPITest(unittest.TestCase):
|
||||||
|
|
||||||
# Issue #15738: crash in subprocess_fork_exec()
|
# Issue #15738: crash in subprocess_fork_exec()
|
||||||
self.assertRaises(TypeError, _posixsubprocess.fork_exec,
|
self.assertRaises(TypeError, _posixsubprocess.fork_exec,
|
||||||
Z(),[b'1'],True,(1, 2),5,6,7,8,9,10,11,12,13,14,True,True,17,False,19,20,21,22,False)
|
Z(),[b'1'],True,(1, 2),5,6,7,8,9,10,11,12,13,14,True,True,17,False,19,20,21,22)
|
||||||
|
|
||||||
@unittest.skipIf(MISSING_C_DOCSTRINGS,
|
@unittest.skipIf(MISSING_C_DOCSTRINGS,
|
||||||
"Signature information for builtins requires docstrings")
|
"Signature information for builtins requires docstrings")
|
||||||
|
|
|
@ -3278,7 +3278,7 @@ class POSIXProcessTestCase(BaseTestCase):
|
||||||
1, 2, 3, 4,
|
1, 2, 3, 4,
|
||||||
True, True, 0,
|
True, True, 0,
|
||||||
None, None, None, -1,
|
None, None, None, -1,
|
||||||
None, True)
|
None)
|
||||||
self.assertIn('fds_to_keep', str(c.exception))
|
self.assertIn('fds_to_keep', str(c.exception))
|
||||||
finally:
|
finally:
|
||||||
if not gc_enabled:
|
if not gc_enabled:
|
||||||
|
@ -3413,25 +3413,6 @@ class POSIXProcessTestCase(BaseTestCase):
|
||||||
self.assertEqual(out.strip(), b"OK")
|
self.assertEqual(out.strip(), b"OK")
|
||||||
self.assertIn(b"preexec_fn not supported at interpreter shutdown", err)
|
self.assertIn(b"preexec_fn not supported at interpreter shutdown", err)
|
||||||
|
|
||||||
@unittest.skipIf(not sysconfig.get_config_var("HAVE_VFORK"),
|
|
||||||
"vfork() not enabled by configure.")
|
|
||||||
@mock.patch("subprocess._fork_exec")
|
|
||||||
@mock.patch("subprocess._USE_POSIX_SPAWN", new=False)
|
|
||||||
def test__use_vfork(self, mock_fork_exec):
|
|
||||||
self.assertTrue(subprocess._USE_VFORK) # The default value regardless.
|
|
||||||
mock_fork_exec.side_effect = RuntimeError("just testing args")
|
|
||||||
with self.assertRaises(RuntimeError):
|
|
||||||
subprocess.run([sys.executable, "-c", "pass"])
|
|
||||||
mock_fork_exec.assert_called_once()
|
|
||||||
# NOTE: These assertions are *ugly* as they require the last arg
|
|
||||||
# to remain the have_vfork boolean. We really need to refactor away
|
|
||||||
# from the giant "wall of args" internal C extension API.
|
|
||||||
self.assertTrue(mock_fork_exec.call_args.args[-1])
|
|
||||||
with mock.patch.object(subprocess, '_USE_VFORK', False):
|
|
||||||
with self.assertRaises(RuntimeError):
|
|
||||||
subprocess.run([sys.executable, "-c", "pass"])
|
|
||||||
self.assertFalse(mock_fork_exec.call_args_list[-1].args[-1])
|
|
||||||
|
|
||||||
@unittest.skipIf(not sysconfig.get_config_var("HAVE_VFORK"),
|
@unittest.skipIf(not sysconfig.get_config_var("HAVE_VFORK"),
|
||||||
"vfork() not enabled by configure.")
|
"vfork() not enabled by configure.")
|
||||||
@unittest.skipIf(sys.platform != "linux", "Linux only, requires strace.")
|
@unittest.skipIf(sys.platform != "linux", "Linux only, requires strace.")
|
||||||
|
@ -3478,7 +3459,6 @@ class POSIXProcessTestCase(BaseTestCase):
|
||||||
# Test that each individual thing that would disable the use of vfork
|
# Test that each individual thing that would disable the use of vfork
|
||||||
# actually disables it.
|
# actually disables it.
|
||||||
for sub_name, preamble, sp_kwarg, expect_permission_error in (
|
for sub_name, preamble, sp_kwarg, expect_permission_error in (
|
||||||
("!use_vfork", "subprocess._USE_VFORK = False", "", False),
|
|
||||||
("preexec", "", "preexec_fn=lambda: None", False),
|
("preexec", "", "preexec_fn=lambda: None", False),
|
||||||
("setgid", "", f"group={os.getgid()}", True),
|
("setgid", "", f"group={os.getgid()}", True),
|
||||||
("setuid", "", f"user={os.getuid()}", True),
|
("setuid", "", f"user={os.getuid()}", True),
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Remove ``subprocess._USE_VFORK`` escape hatch code and documentation.
|
||||||
|
It was added just in case, and doesn't have any known cases that require it.
|
|
@ -977,7 +977,6 @@ _posixsubprocess.fork_exec as subprocess_fork_exec
|
||||||
uid as uid_object: object
|
uid as uid_object: object
|
||||||
child_umask: int
|
child_umask: int
|
||||||
preexec_fn: object
|
preexec_fn: object
|
||||||
allow_vfork: bool
|
|
||||||
/
|
/
|
||||||
|
|
||||||
Spawn a fresh new child process.
|
Spawn a fresh new child process.
|
||||||
|
@ -1014,8 +1013,8 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args,
|
||||||
pid_t pgid_to_set, PyObject *gid_object,
|
pid_t pgid_to_set, PyObject *gid_object,
|
||||||
PyObject *extra_groups_packed,
|
PyObject *extra_groups_packed,
|
||||||
PyObject *uid_object, int child_umask,
|
PyObject *uid_object, int child_umask,
|
||||||
PyObject *preexec_fn, int allow_vfork)
|
PyObject *preexec_fn)
|
||||||
/*[clinic end generated code: output=7ee4f6ee5cf22b5b input=51757287ef266ffa]*/
|
/*[clinic end generated code: output=288464dc56e373c7 input=f311c3bcb5dd55c8]*/
|
||||||
{
|
{
|
||||||
PyObject *converted_args = NULL, *fast_args = NULL;
|
PyObject *converted_args = NULL, *fast_args = NULL;
|
||||||
PyObject *preexec_fn_args_tuple = NULL;
|
PyObject *preexec_fn_args_tuple = NULL;
|
||||||
|
@ -1218,7 +1217,7 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args,
|
||||||
#ifdef VFORK_USABLE
|
#ifdef VFORK_USABLE
|
||||||
/* Use vfork() only if it's safe. See the comment above child_exec(). */
|
/* Use vfork() only if it's safe. See the comment above child_exec(). */
|
||||||
sigset_t old_sigs;
|
sigset_t old_sigs;
|
||||||
if (preexec_fn == Py_None && allow_vfork &&
|
if (preexec_fn == Py_None &&
|
||||||
uid == (uid_t)-1 && gid == (gid_t)-1 && extra_group_size < 0) {
|
uid == (uid_t)-1 && gid == (gid_t)-1 && extra_group_size < 0) {
|
||||||
/* Block all signals to ensure that no signal handlers are run in the
|
/* Block all signals to ensure that no signal handlers are run in the
|
||||||
* child process while it shares memory with us. Note that signals
|
* child process while it shares memory with us. Note that signals
|
||||||
|
|
|
@ -9,7 +9,7 @@ PyDoc_STRVAR(subprocess_fork_exec__doc__,
|
||||||
" env, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite,\n"
|
" env, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite,\n"
|
||||||
" errpipe_read, errpipe_write, restore_signals, call_setsid,\n"
|
" errpipe_read, errpipe_write, restore_signals, call_setsid,\n"
|
||||||
" pgid_to_set, gid, extra_groups, uid, child_umask, preexec_fn,\n"
|
" pgid_to_set, gid, extra_groups, uid, child_umask, preexec_fn,\n"
|
||||||
" allow_vfork, /)\n"
|
" /)\n"
|
||||||
"--\n"
|
"--\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Spawn a fresh new child process.\n"
|
"Spawn a fresh new child process.\n"
|
||||||
|
@ -48,7 +48,7 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args,
|
||||||
pid_t pgid_to_set, PyObject *gid_object,
|
pid_t pgid_to_set, PyObject *gid_object,
|
||||||
PyObject *extra_groups_packed,
|
PyObject *extra_groups_packed,
|
||||||
PyObject *uid_object, int child_umask,
|
PyObject *uid_object, int child_umask,
|
||||||
PyObject *preexec_fn, int allow_vfork);
|
PyObject *preexec_fn);
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
subprocess_fork_exec(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
subprocess_fork_exec(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||||
|
@ -76,9 +76,8 @@ subprocess_fork_exec(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||||
PyObject *uid_object;
|
PyObject *uid_object;
|
||||||
int child_umask;
|
int child_umask;
|
||||||
PyObject *preexec_fn;
|
PyObject *preexec_fn;
|
||||||
int allow_vfork;
|
|
||||||
|
|
||||||
if (!_PyArg_CheckPositional("fork_exec", nargs, 23, 23)) {
|
if (!_PyArg_CheckPositional("fork_exec", nargs, 22, 22)) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
process_args = args[0];
|
process_args = args[0];
|
||||||
|
@ -146,13 +145,9 @@ subprocess_fork_exec(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
preexec_fn = args[21];
|
preexec_fn = args[21];
|
||||||
allow_vfork = PyObject_IsTrue(args[22]);
|
return_value = subprocess_fork_exec_impl(module, process_args, executable_list, close_fds, py_fds_to_keep, cwd_obj, env_list, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, errpipe_read, errpipe_write, restore_signals, call_setsid, pgid_to_set, gid_object, extra_groups_packed, uid_object, child_umask, preexec_fn);
|
||||||
if (allow_vfork < 0) {
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
return_value = subprocess_fork_exec_impl(module, process_args, executable_list, close_fds, py_fds_to_keep, cwd_obj, env_list, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, errpipe_read, errpipe_write, restore_signals, call_setsid, pgid_to_set, gid_object, extra_groups_packed, uid_object, child_umask, preexec_fn, allow_vfork);
|
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
/*[clinic end generated code: output=48555f5965a871be input=a9049054013a1b77]*/
|
/*[clinic end generated code: output=942bc2748a9c2785 input=a9049054013a1b77]*/
|
||||||
|
|
Loading…
Reference in New Issue