Skip to content

Commit 506f0db

Browse files
eendebakptclaude
andcommitted
gh-151060: Fix sum() deallocating a float subclass as an exact float
The complex-total fast path in builtin_sum_impl() guarded with PyFloat_Check() (which accepts subclasses) but freed the item with _Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc), which assumes an exact float. A float subclass instance reaching refcount 0 in that loop aborts on an assertion in debug builds, and in release builds skips the subclass tp_dealloc (type refcount leak, __del__ never runs, subclass object pushed onto the float freelist). Use PyFloat_CheckExact() instead, matching the float-total branch, so subclass instances fall through to the generic PyNumber_Add() path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4084141 commit 506f0db

3 files changed

Lines changed: 15 additions & 1 deletion

File tree

‎Lib/test/test_builtin.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2229,6 +2229,18 @@ def __getitem__(self, index):
22292229
self.assertComplexesAreIdentical(sum([1.0, complex(1, -0.0)]),
22302230
complex(2, -0.0))
22312231

2232+
def test_sum_float_subclass(self):
2233+
# gh-151060: the C fast-sum loop must not free a float subclass with
2234+
# the exact-float deallocator. A generator keeps each item's only
2235+
# reference in sum(), so the specialized DECREF runs the deallocator.
2236+
class F(float):
2237+
count = 0
2238+
def __del__(self):
2239+
F.count += 1
2240+
2241+
self.assertEqual(sum((F(i) for i in range(5)), 1j), complex(10, 1))
2242+
self.assertEqual(F.count, 5)
2243+
22322244
@requires_IEEE_754
22332245
@skip_if_double_rounding
22342246
@support.cpython_only # Other implementations may choose a different algorithm
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a crash or memory corruption in :func:`sum` when summing a
2+
:class:`float` subclass instance into a complex running total.

‎Python/bltinmodule.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3046,7 +3046,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
30463046
return NULL;
30473047
}
30483048
}
3049-
if (PyFloat_Check(item)) {
3049+
if (PyFloat_CheckExact(item)) {
30503050
double value = PyFloat_AS_DOUBLE(item);
30513051
re_sum = cs_add(re_sum, value);
30523052
_Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc);

0 commit comments

Comments
 (0)