bpo-30065: Fixed arguments validation in _posixsubprocess.fork_exec(). (#1110)
This commit is contained in:
parent
a79f4c2195
commit
66bffd1663
|
@ -386,7 +386,7 @@ def _close_stdin():
|
||||||
|
|
||||||
def spawnv_passfds(path, args, passfds):
|
def spawnv_passfds(path, args, passfds):
|
||||||
import _posixsubprocess
|
import _posixsubprocess
|
||||||
passfds = sorted(passfds)
|
passfds = tuple(sorted(map(int, passfds)))
|
||||||
errpipe_read, errpipe_write = os.pipe()
|
errpipe_read, errpipe_write = os.pipe()
|
||||||
try:
|
try:
|
||||||
return _posixsubprocess.fork_exec(
|
return _posixsubprocess.fork_exec(
|
||||||
|
|
|
@ -1252,7 +1252,8 @@ class Popen(object):
|
||||||
fds_to_keep.add(errpipe_write)
|
fds_to_keep.add(errpipe_write)
|
||||||
self.pid = _posixsubprocess.fork_exec(
|
self.pid = _posixsubprocess.fork_exec(
|
||||||
args, executable_list,
|
args, executable_list,
|
||||||
close_fds, sorted(fds_to_keep), cwd, env_list,
|
close_fds, tuple(sorted(map(int, fds_to_keep))),
|
||||||
|
cwd, env_list,
|
||||||
p2cread, p2cwrite, c2pread, c2pwrite,
|
p2cread, p2cwrite, c2pread, c2pwrite,
|
||||||
errread, errwrite,
|
errread, errwrite,
|
||||||
errpipe_read, errpipe_write,
|
errpipe_read, errpipe_write,
|
||||||
|
|
|
@ -98,7 +98,7 @@ class CAPITest(unittest.TestCase):
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return 1
|
return 1
|
||||||
self.assertRaises(TypeError, _posixsubprocess.fork_exec,
|
self.assertRaises(TypeError, _posixsubprocess.fork_exec,
|
||||||
1,Z(),3,[1, 2],5,6,7,8,9,10,11,12,13,14,15,16,17)
|
1,Z(),3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17)
|
||||||
# Issue #15736: overflow in _PySequence_BytesToCharpArray()
|
# Issue #15736: overflow in _PySequence_BytesToCharpArray()
|
||||||
class Z(object):
|
class Z(object):
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
|
@ -106,7 +106,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(),3,[1, 2],5,6,7,8,9,10,11,12,13,14,15,16,17)
|
1,Z(),3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17)
|
||||||
|
|
||||||
@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):
|
||||||
|
@ -116,7 +116,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'],3,[1, 2],5,6,7,8,9,10,11,12,13,14,15,16,17)
|
Z(),[b'1'],3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17)
|
||||||
|
|
||||||
@unittest.skipIf(MISSING_C_DOCSTRINGS,
|
@unittest.skipIf(MISSING_C_DOCSTRINGS,
|
||||||
"Signature information for builtins requires docstrings")
|
"Signature information for builtins requires docstrings")
|
||||||
|
|
|
@ -2418,7 +2418,7 @@ class POSIXProcessTestCase(BaseTestCase):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
_posixsubprocess.fork_exec(
|
_posixsubprocess.fork_exec(
|
||||||
args, exe_list,
|
args, exe_list,
|
||||||
True, [], cwd, env_list,
|
True, (), cwd, env_list,
|
||||||
-1, -1, -1, -1,
|
-1, -1, -1, -1,
|
||||||
1, 2, 3, 4,
|
1, 2, 3, 4,
|
||||||
True, True, func)
|
True, True, func)
|
||||||
|
@ -2430,6 +2430,16 @@ class POSIXProcessTestCase(BaseTestCase):
|
||||||
def test_fork_exec_sorted_fd_sanity_check(self):
|
def test_fork_exec_sorted_fd_sanity_check(self):
|
||||||
# Issue #23564: sanity check the fork_exec() fds_to_keep sanity check.
|
# Issue #23564: sanity check the fork_exec() fds_to_keep sanity check.
|
||||||
import _posixsubprocess
|
import _posixsubprocess
|
||||||
|
class BadInt:
|
||||||
|
first = True
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
def __int__(self):
|
||||||
|
if self.first:
|
||||||
|
self.first = False
|
||||||
|
return self.value
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
gc_enabled = gc.isenabled()
|
gc_enabled = gc.isenabled()
|
||||||
try:
|
try:
|
||||||
gc.enable()
|
gc.enable()
|
||||||
|
@ -2440,6 +2450,7 @@ class POSIXProcessTestCase(BaseTestCase):
|
||||||
(18, 23, 42, 2**63), # Out of range.
|
(18, 23, 42, 2**63), # Out of range.
|
||||||
(5, 4), # Not sorted.
|
(5, 4), # Not sorted.
|
||||||
(6, 7, 7, 8), # Duplicate.
|
(6, 7, 7, 8), # Duplicate.
|
||||||
|
(BadInt(1), BadInt(2)),
|
||||||
):
|
):
|
||||||
with self.assertRaises(
|
with self.assertRaises(
|
||||||
ValueError,
|
ValueError,
|
||||||
|
|
|
@ -111,13 +111,17 @@ _is_fdescfs_mounted_on_dev_fd(void)
|
||||||
static int
|
static int
|
||||||
_sanity_check_python_fd_sequence(PyObject *fd_sequence)
|
_sanity_check_python_fd_sequence(PyObject *fd_sequence)
|
||||||
{
|
{
|
||||||
Py_ssize_t seq_idx, seq_len = PySequence_Length(fd_sequence);
|
Py_ssize_t seq_idx;
|
||||||
long prev_fd = -1;
|
long prev_fd = -1;
|
||||||
for (seq_idx = 0; seq_idx < seq_len; ++seq_idx) {
|
for (seq_idx = 0; seq_idx < PyTuple_GET_SIZE(fd_sequence); ++seq_idx) {
|
||||||
PyObject* py_fd = PySequence_Fast_GET_ITEM(fd_sequence, seq_idx);
|
PyObject* py_fd = PyTuple_GET_ITEM(fd_sequence, seq_idx);
|
||||||
long iter_fd = PyLong_AsLong(py_fd);
|
long iter_fd;
|
||||||
|
if (!PyLong_Check(py_fd)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
iter_fd = PyLong_AsLong(py_fd);
|
||||||
if (iter_fd < 0 || iter_fd <= prev_fd || iter_fd > INT_MAX) {
|
if (iter_fd < 0 || iter_fd <= prev_fd || iter_fd > INT_MAX) {
|
||||||
/* Negative, overflow, not a Long, unsorted, too big for a fd. */
|
/* Negative, overflow, unsorted, too big for a fd. */
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
prev_fd = iter_fd;
|
prev_fd = iter_fd;
|
||||||
|
@ -132,13 +136,12 @@ _is_fd_in_sorted_fd_sequence(int fd, PyObject *fd_sequence)
|
||||||
{
|
{
|
||||||
/* Binary search. */
|
/* Binary search. */
|
||||||
Py_ssize_t search_min = 0;
|
Py_ssize_t search_min = 0;
|
||||||
Py_ssize_t search_max = PySequence_Length(fd_sequence) - 1;
|
Py_ssize_t search_max = PyTuple_GET_SIZE(fd_sequence) - 1;
|
||||||
if (search_max < 0)
|
if (search_max < 0)
|
||||||
return 0;
|
return 0;
|
||||||
do {
|
do {
|
||||||
long middle = (search_min + search_max) / 2;
|
long middle = (search_min + search_max) / 2;
|
||||||
long middle_fd = PyLong_AsLong(
|
long middle_fd = PyLong_AsLong(PyTuple_GET_ITEM(fd_sequence, middle));
|
||||||
PySequence_Fast_GET_ITEM(fd_sequence, middle));
|
|
||||||
if (fd == middle_fd)
|
if (fd == middle_fd)
|
||||||
return 1;
|
return 1;
|
||||||
if (fd > middle_fd)
|
if (fd > middle_fd)
|
||||||
|
@ -154,9 +157,9 @@ make_inheritable(PyObject *py_fds_to_keep, int errpipe_write)
|
||||||
{
|
{
|
||||||
Py_ssize_t i, len;
|
Py_ssize_t i, len;
|
||||||
|
|
||||||
len = PySequence_Length(py_fds_to_keep);
|
len = PyTuple_GET_SIZE(py_fds_to_keep);
|
||||||
for (i = 0; i < len; ++i) {
|
for (i = 0; i < len; ++i) {
|
||||||
PyObject* fdobj = PySequence_Fast_GET_ITEM(py_fds_to_keep, i);
|
PyObject* fdobj = PyTuple_GET_ITEM(py_fds_to_keep, i);
|
||||||
long fd = PyLong_AsLong(fdobj);
|
long fd = PyLong_AsLong(fdobj);
|
||||||
assert(!PyErr_Occurred());
|
assert(!PyErr_Occurred());
|
||||||
assert(0 <= fd && fd <= INT_MAX);
|
assert(0 <= fd && fd <= INT_MAX);
|
||||||
|
@ -213,14 +216,13 @@ static void
|
||||||
_close_fds_by_brute_force(long start_fd, PyObject *py_fds_to_keep)
|
_close_fds_by_brute_force(long start_fd, PyObject *py_fds_to_keep)
|
||||||
{
|
{
|
||||||
long end_fd = safe_get_max_fd();
|
long end_fd = safe_get_max_fd();
|
||||||
Py_ssize_t num_fds_to_keep = PySequence_Length(py_fds_to_keep);
|
Py_ssize_t num_fds_to_keep = PyTuple_GET_SIZE(py_fds_to_keep);
|
||||||
Py_ssize_t keep_seq_idx;
|
Py_ssize_t keep_seq_idx;
|
||||||
int fd_num;
|
int fd_num;
|
||||||
/* As py_fds_to_keep is sorted we can loop through the list closing
|
/* As py_fds_to_keep is sorted we can loop through the list closing
|
||||||
* fds inbetween any in the keep list falling within our range. */
|
* fds inbetween any in the keep list falling within our range. */
|
||||||
for (keep_seq_idx = 0; keep_seq_idx < num_fds_to_keep; ++keep_seq_idx) {
|
for (keep_seq_idx = 0; keep_seq_idx < num_fds_to_keep; ++keep_seq_idx) {
|
||||||
PyObject* py_keep_fd = PySequence_Fast_GET_ITEM(py_fds_to_keep,
|
PyObject* py_keep_fd = PyTuple_GET_ITEM(py_fds_to_keep, keep_seq_idx);
|
||||||
keep_seq_idx);
|
|
||||||
int keep_fd = PyLong_AsLong(py_keep_fd);
|
int keep_fd = PyLong_AsLong(py_keep_fd);
|
||||||
if (keep_fd < start_fd)
|
if (keep_fd < start_fd)
|
||||||
continue;
|
continue;
|
||||||
|
@ -306,7 +308,7 @@ _close_open_fds_safe(int start_fd, PyObject* py_fds_to_keep)
|
||||||
|
|
||||||
|
|
||||||
/* Close all open file descriptors from start_fd and higher.
|
/* Close all open file descriptors from start_fd and higher.
|
||||||
* Do not close any in the sorted py_fds_to_keep list.
|
* Do not close any in the sorted py_fds_to_keep tuple.
|
||||||
*
|
*
|
||||||
* This function violates the strict use of async signal safe functions. :(
|
* This function violates the strict use of async signal safe functions. :(
|
||||||
* It calls opendir(), readdir() and closedir(). Of these, the one most
|
* It calls opendir(), readdir() and closedir(). Of these, the one most
|
||||||
|
@ -562,8 +564,9 @@ subprocess_fork_exec(PyObject* self, PyObject *args)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(
|
if (!PyArg_ParseTuple(
|
||||||
args, "OOpOOOiiiiiiiiiiO:fork_exec",
|
args, "OOpO!OOiiiiiiiiiiO:fork_exec",
|
||||||
&process_args, &executable_list, &close_fds, &py_fds_to_keep,
|
&process_args, &executable_list,
|
||||||
|
&close_fds, &PyTuple_Type, &py_fds_to_keep,
|
||||||
&cwd_obj, &env_list,
|
&cwd_obj, &env_list,
|
||||||
&p2cread, &p2cwrite, &c2pread, &c2pwrite,
|
&p2cread, &p2cwrite, &c2pread, &c2pwrite,
|
||||||
&errread, &errwrite, &errpipe_read, &errpipe_write,
|
&errread, &errwrite, &errpipe_read, &errpipe_write,
|
||||||
|
@ -574,10 +577,6 @@ subprocess_fork_exec(PyObject* self, PyObject *args)
|
||||||
PyErr_SetString(PyExc_ValueError, "errpipe_write must be >= 3");
|
PyErr_SetString(PyExc_ValueError, "errpipe_write must be >= 3");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (PySequence_Length(py_fds_to_keep) < 0) {
|
|
||||||
PyErr_SetString(PyExc_ValueError, "cannot get length of fds_to_keep");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (_sanity_check_python_fd_sequence(py_fds_to_keep)) {
|
if (_sanity_check_python_fd_sequence(py_fds_to_keep)) {
|
||||||
PyErr_SetString(PyExc_ValueError, "bad value(s) in fds_to_keep");
|
PyErr_SetString(PyExc_ValueError, "bad value(s) in fds_to_keep");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -631,6 +630,10 @@ subprocess_fork_exec(PyObject* self, PyObject *args)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
for (arg_num = 0; arg_num < num_args; ++arg_num) {
|
for (arg_num = 0; arg_num < num_args; ++arg_num) {
|
||||||
PyObject *borrowed_arg, *converted_arg;
|
PyObject *borrowed_arg, *converted_arg;
|
||||||
|
if (PySequence_Fast_GET_SIZE(fast_args) != num_args) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "args changed during iteration");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
borrowed_arg = PySequence_Fast_GET_ITEM(fast_args, arg_num);
|
borrowed_arg = PySequence_Fast_GET_ITEM(fast_args, arg_num);
|
||||||
if (PyUnicode_FSConverter(borrowed_arg, &converted_arg) == 0)
|
if (PyUnicode_FSConverter(borrowed_arg, &converted_arg) == 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
Loading…
Reference in New Issue