Skip to content

Commit 11b5e0b

Browse files
[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 be5af99) Co-authored-by: Mikhail Efimov <[email protected]>
1 parent a975bea commit 11b5e0b

File tree

2 files changed

+41
-19
lines changed

2 files changed

+41
-19
lines changed

Lib/test/test_builtin.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,22 @@ def test_map_strict(self):
13841384
self.assertRaises(ValueError, tuple,
13851385
map(pack, (1, 2), (1, 2), 'abc', strict=True))
13861386

1387+
# gh-140517: Testing refleaks with mortal objects.
1388+
t1 = (None, object())
1389+
t2 = (object(), object())
1390+
t3 = (object(),)
1391+
1392+
self.assertRaises(ValueError, tuple,
1393+
map(pack, t1, 'a', strict=True))
1394+
self.assertRaises(ValueError, tuple,
1395+
map(pack, t1, t2, 'a', strict=True))
1396+
self.assertRaises(ValueError, tuple,
1397+
map(pack, t1, t2, t3, strict=True))
1398+
self.assertRaises(ValueError, tuple,
1399+
map(pack, 'a', t1, strict=True))
1400+
self.assertRaises(ValueError, tuple,
1401+
map(pack, 'a', t2, t3, strict=True))
1402+
13871403
def test_map_strict_iterators(self):
13881404
x = iter(range(5))
13891405
y = [0]

Python/bltinmodule.c

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,66 +1501,72 @@ map_next(PyObject *self)
15011501
}
15021502

15031503
Py_ssize_t nargs = 0;
1504-
for (i=0; i < niters; i++) {
1504+
for (i = 0; i < niters; i++) {
15051505
PyObject *it = PyTuple_GET_ITEM(lz->iters, i);
15061506
PyObject *val = Py_TYPE(it)->tp_iternext(it);
15071507
if (val == NULL) {
15081508
if (lz->strict) {
15091509
goto check;
15101510
}
1511-
goto exit;
1511+
goto exit_no_result;
15121512
}
15131513
stack[i] = val;
15141514
nargs++;
15151515
}
15161516

15171517
result = _PyObject_VectorcallTstate(tstate, lz->func, stack, nargs, NULL);
1518+
goto exit;
15181519

1519-
exit:
1520-
for (i=0; i < nargs; i++) {
1521-
Py_DECREF(stack[i]);
1522-
}
1523-
if (stack != small_stack) {
1524-
PyMem_Free(stack);
1525-
}
1526-
return result;
15271520
check:
15281521
if (PyErr_Occurred()) {
15291522
if (!PyErr_ExceptionMatches(PyExc_StopIteration)) {
15301523
// next() on argument i raised an exception (not StopIteration)
1531-
return NULL;
1524+
goto exit_no_result;
15321525
}
15331526
PyErr_Clear();
15341527
}
15351528
if (i) {
15361529
// ValueError: map() argument 2 is shorter than argument 1
15371530
// ValueError: map() argument 3 is shorter than arguments 1-2
15381531
const char* plural = i == 1 ? " " : "s 1-";
1539-
return PyErr_Format(PyExc_ValueError,
1540-
"map() argument %d is shorter than argument%s%d",
1541-
i + 1, plural, i);
1532+
PyErr_Format(PyExc_ValueError,
1533+
"map() argument %d is shorter than argument%s%d",
1534+
i + 1, plural, i);
1535+
goto exit_no_result;
15421536
}
15431537
for (i = 1; i < niters; i++) {
15441538
PyObject *it = PyTuple_GET_ITEM(lz->iters, i);
15451539
PyObject *val = (*Py_TYPE(it)->tp_iternext)(it);
15461540
if (val) {
15471541
Py_DECREF(val);
15481542
const char* plural = i == 1 ? " " : "s 1-";
1549-
return PyErr_Format(PyExc_ValueError,
1550-
"map() argument %d is longer than argument%s%d",
1551-
i + 1, plural, i);
1543+
PyErr_Format(PyExc_ValueError,
1544+
"map() argument %d is longer than argument%s%d",
1545+
i + 1, plural, i);
1546+
goto exit_no_result;
15521547
}
15531548
if (PyErr_Occurred()) {
15541549
if (!PyErr_ExceptionMatches(PyExc_StopIteration)) {
15551550
// next() on argument i raised an exception (not StopIteration)
1556-
return NULL;
1551+
goto exit_no_result;
15571552
}
15581553
PyErr_Clear();
15591554
}
15601555
// Argument i is exhausted. So far so good...
15611556
}
15621557
// All arguments are exhausted. Success!
1563-
goto exit;
1558+
1559+
exit_no_result:
1560+
assert(result == NULL);
1561+
1562+
exit:
1563+
for (i = 0; i < nargs; i++) {
1564+
Py_DECREF(stack[i]);
1565+
}
1566+
if (stack != small_stack) {
1567+
PyMem_Free(stack);
1568+
}
1569+
return result;
15641570
}
15651571

15661572
static PyObject *

0 commit comments

Comments
 (0)