[New-bugs-announce] [issue24469] Py2.x int free list can grow without bounds

Stefan Behnel report at bugs.python.org
Fri Jun 19 08:31:18 CEST 2015


New submission from Stefan Behnel:

A Cython user noticed a memory leak when C-inheriting from "int".

http://thread.gmane.org/gmane.comp.python.cython.devel/15689

The Cython code to reproduce this is simply this:

    cdef class ExtendedInt(int): pass
 
    for j in xrange(10000000):
        ExtendedInt(j)

The problem is due to the free-list of the int type. It uses this code for deallocation:

"""
static void
int_dealloc(PyIntObject *v)
{
    if (PyInt_CheckExact(v)) {
        Py_TYPE(v) = (struct _typeobject *)free_list;
        free_list = v;
    }
    else
        Py_TYPE(v)->tp_free((PyObject *)v);
}

static void
int_free(PyIntObject *v)
{
    Py_TYPE(v) = (struct _typeobject *)free_list;
    free_list = v;
}
"""

Now, when C-inheriting from PyInt_Type without providing an own tp_free implementation, PyType_Ready() will inherit the supertype's tp_free slot, which means that int_dealloc() above will end up calling int_free() in all cases, not only for the exact-int case. Thus, whether or not it's exactly "int" or a subtype, the object will always be added to the free-list on deallocation.

However, in the subtype case, the free-list is actually ignored by int_new() and int_subtype_new(), so that as long as the user code only creates tons of int subtype instances and not plain int instances, the freelist will keep growing without bounds by pushing dead subtype objects onto it that are never reused.

There are two problems here:

1) int subtypes should not be added to the freelist, or at least not unless they have exactly the same struct size as PyIntObject (which the ExtendedInt type above does but other subtypes may not)

2) if a free-list is used, it should be used in all instantiation cases, not just in PyInt_FromLong().

Fixing 1) by adding a type check to int_free() would be enough to fix the overall problem. Here's a quickly hacked up change that seems to work for me:

"""
static void
int_free(PyIntObject *v)
{
    if (PyInt_CheckExact(v)) {
        Py_TYPE(v) = (struct _typeobject *)free_list;
        free_list = v;
    }
    else if (Py_TYPE(v)->tp_flags & Py_TPFLAGS_HAVE_GC)
        PyObject_GC_Del(v);  // untested by probably necessary
    else
        PyObject_Del(v);
}
"""

----------
components: Interpreter Core
messages: 245492
nosy: scoder
priority: normal
severity: normal
status: open
title: Py2.x int free list can grow without bounds
type: crash
versions: Python 2.7

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue24469>
_______________________________________


More information about the New-bugs-announce mailing list