[Cython] Metaclass bug corrupts the heap

Dénes Vadász python2 at dvadasz.hu
Wed Sep 11 00:26:01 CEST 2013


Hello,

according to valgrind the following Cython fragment causes a heap 
corruption (with Python 2.7.5 and Cython 0.19.1 on Ubuntu):

    cdef class MetaClass(type):
         cdef int i

    class MyClass(object):
         __metaclass__ = MetaClass



Please find below the result of a many hour investigation (originally 
triggered by random crashes and heap corruptions of a bigger Cython 
program that uses metaclasses).

MetaClass is compiled to a C structure that extends PyTypeObject (a cc. 
200 byte structure on a 32-bit architecture):

    struct __pyx_obj_4meta_MetaClass {
       PyTypeObject __pyx_base;
       int i;
    };


Instances of PyTypeObject are supposed to be allocated statically when 
initializing extension modules, because the structure does not support 
(among others) garbage collection. However, when MyClass is created, an 
instance of struct __pyx_obj_4meta_MetaClass (cc. 200 + 4 bytes) is 
dynamically allocated by the Python memory management machinery. The 
machinery then tries to initialize the allocated memory. The problem is 
that it expects that the first member of struct 
__pyx_obj_4meta_MetaClass is of type PyHeapTypeObject (a cc. 500 byte 
structure) and after a type cast it writes to the tail members of the 
assumed PyHeapTypeObject, i.e. way beyond the allocated cc. 200 + 4 
bytes, corrupting the object heap. This corruption is nicely reported by 
valgrind.

The proposed fix is to make __pyx_obj_4meta_MetaClass extend 
PyHeapTypeObject:

    struct __pyx_obj_4meta_MetaClass {
       PyHeapTypeObject __pyx_base;
       int i;
    };


This can be achieved by adding the below 2 lines to Compiler/Builtin.py:

    382a383,384
     >         elif name == 'type':
     >             objstruct_cname = 'PyHeapTypeObject'


So that the init_builtin_types function becomes:

    def init_builtin_types():
         global builtin_types
         for name, cname, methods in builtin_types_table:
             utility = builtin_utility_code.get(name)
             if name == 'frozenset':
                 objstruct_cname = 'PySetObject'
             elif name == 'bool':
                 objstruct_cname = None
    *       elif name == 'type':*
    *           objstruct_cname = 'PyHeapTypeObject'*
             else:
                 objstruct_cname = 'Py%sObject' % name.capitalize()
             the_type = builtin_scope.declare_builtin_type(name, cname,
    utility, objstruct_cname)
             builtin_types[name] = the_type
             for method in methods:
                 method.declare_in_type(the_type)


After patching my Cython installation with the above, valgrind stopped 
to complain and there were no more crashes.

Please consider adding this fix in the next release of Cython.

Regards

Dénes Vadász
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/cython-devel/attachments/20130911/289cceae/attachment.html>


More information about the cython-devel mailing list