[3.14] gh-140517: fix leak in map_next in strict mode (GH-140543) (#140554)

gh-140517: fix leak in `map_next` in strict mode (GH-140543)
(cherry picked from commit be5af997f3)

Co-authored-by: Mikhail Efimov <efimov.mikhail@gmail.com>
This commit is contained in:
Miss Islington (bot) 2025-10-24 18:30:36 +02:00 committed by GitHub
parent a975bea9b5
commit 11b5e0b9c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 41 additions and 19 deletions

View file

@ -1384,6 +1384,22 @@ def test_map_strict(self):
self.assertRaises(ValueError, tuple, self.assertRaises(ValueError, tuple,
map(pack, (1, 2), (1, 2), 'abc', strict=True)) map(pack, (1, 2), (1, 2), 'abc', strict=True))
# gh-140517: Testing refleaks with mortal objects.
t1 = (None, object())
t2 = (object(), object())
t3 = (object(),)
self.assertRaises(ValueError, tuple,
map(pack, t1, 'a', strict=True))
self.assertRaises(ValueError, tuple,
map(pack, t1, t2, 'a', strict=True))
self.assertRaises(ValueError, tuple,
map(pack, t1, t2, t3, strict=True))
self.assertRaises(ValueError, tuple,
map(pack, 'a', t1, strict=True))
self.assertRaises(ValueError, tuple,
map(pack, 'a', t2, t3, strict=True))
def test_map_strict_iterators(self): def test_map_strict_iterators(self):
x = iter(range(5)) x = iter(range(5))
y = [0] y = [0]

View file

@ -1501,34 +1501,27 @@ map_next(PyObject *self)
} }
Py_ssize_t nargs = 0; Py_ssize_t nargs = 0;
for (i=0; i < niters; i++) { for (i = 0; i < niters; i++) {
PyObject *it = PyTuple_GET_ITEM(lz->iters, i); PyObject *it = PyTuple_GET_ITEM(lz->iters, i);
PyObject *val = Py_TYPE(it)->tp_iternext(it); PyObject *val = Py_TYPE(it)->tp_iternext(it);
if (val == NULL) { if (val == NULL) {
if (lz->strict) { if (lz->strict) {
goto check; goto check;
} }
goto exit; goto exit_no_result;
} }
stack[i] = val; stack[i] = val;
nargs++; nargs++;
} }
result = _PyObject_VectorcallTstate(tstate, lz->func, stack, nargs, NULL); result = _PyObject_VectorcallTstate(tstate, lz->func, stack, nargs, NULL);
goto exit;
exit:
for (i=0; i < nargs; i++) {
Py_DECREF(stack[i]);
}
if (stack != small_stack) {
PyMem_Free(stack);
}
return result;
check: check:
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
if (!PyErr_ExceptionMatches(PyExc_StopIteration)) { if (!PyErr_ExceptionMatches(PyExc_StopIteration)) {
// next() on argument i raised an exception (not StopIteration) // next() on argument i raised an exception (not StopIteration)
return NULL; goto exit_no_result;
} }
PyErr_Clear(); PyErr_Clear();
} }
@ -1536,9 +1529,10 @@ map_next(PyObject *self)
// ValueError: map() argument 2 is shorter than argument 1 // ValueError: map() argument 2 is shorter than argument 1
// ValueError: map() argument 3 is shorter than arguments 1-2 // ValueError: map() argument 3 is shorter than arguments 1-2
const char* plural = i == 1 ? " " : "s 1-"; const char* plural = i == 1 ? " " : "s 1-";
return PyErr_Format(PyExc_ValueError, PyErr_Format(PyExc_ValueError,
"map() argument %d is shorter than argument%s%d", "map() argument %d is shorter than argument%s%d",
i + 1, plural, i); i + 1, plural, i);
goto exit_no_result;
} }
for (i = 1; i < niters; i++) { for (i = 1; i < niters; i++) {
PyObject *it = PyTuple_GET_ITEM(lz->iters, i); PyObject *it = PyTuple_GET_ITEM(lz->iters, i);
@ -1546,21 +1540,33 @@ map_next(PyObject *self)
if (val) { if (val) {
Py_DECREF(val); Py_DECREF(val);
const char* plural = i == 1 ? " " : "s 1-"; const char* plural = i == 1 ? " " : "s 1-";
return PyErr_Format(PyExc_ValueError, PyErr_Format(PyExc_ValueError,
"map() argument %d is longer than argument%s%d", "map() argument %d is longer than argument%s%d",
i + 1, plural, i); i + 1, plural, i);
goto exit_no_result;
} }
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
if (!PyErr_ExceptionMatches(PyExc_StopIteration)) { if (!PyErr_ExceptionMatches(PyExc_StopIteration)) {
// next() on argument i raised an exception (not StopIteration) // next() on argument i raised an exception (not StopIteration)
return NULL; goto exit_no_result;
} }
PyErr_Clear(); PyErr_Clear();
} }
// Argument i is exhausted. So far so good... // Argument i is exhausted. So far so good...
} }
// All arguments are exhausted. Success! // All arguments are exhausted. Success!
goto exit;
exit_no_result:
assert(result == NULL);
exit:
for (i = 0; i < nargs; i++) {
Py_DECREF(stack[i]);
}
if (stack != small_stack) {
PyMem_Free(stack);
}
return result;
} }
static PyObject * static PyObject *