slpmodule_new() doesn't initialize extra members added by type_new() #22

Description
Originally reported by: RMTEW FULL NAME (Bitbucket: rmtew, GitHub: rmtew)
(originally reported in Trac by ndade on 2013-06-08 02:47:27)
slpmodule_new() allocates the module using
m = PyObject_GC_New(PySlpModuleObject, PySlpModule_TypePtr);
and that uses PySlpModule_TypePtr->tp_basicsize to determine how many bytes to allocate. On amd64, for example, we expect there to be 40 bytes allocated (sizeof(*m)), but in fact 48 are allocated[1] because tp_basicsize was set to 48 back when the SLP module type was derived in flextype.c. type_new() computed 48 by taking the base type's tp_basicsize of 40 and adding space for one more pointer to hold the weak reference list. Grep for "may_add_weak" in type_new() for the details.
Since this silently added field in *m is not initialized, when the slpmodule object is cleaned up during exit, if that field had a non-NULL value that value gets interpreted as the head of a list of PyWeakReference objects and garbage ensues.
Apparently with the default memory allocators, and perhaps since slpmodule is allocated pretty early, it was NULL. But I've been replacing the GC allocator with my own and there the memory was recycled and the pointer was non-NULL.
The fix is to init the entire memory/object allocated. Something like
@@ -806,6 +806,8 @@
m->__tasklet__ = NULL;
nameobj = PyString_FromString(name);
m->md_dict = PyDict_New();
+ // set the extra fields to 0 (type_new() added a weak reference list field automatically)
+ memset(m+1, 0, PySlpModule_TypePtr->tp_basicsize - sizeof(*m));
if (m->md_dict == NULL || nameobj == NULL)
goto fail;
if (PyDict_SetItemString(m->md_dict, "__name__", nameobj) != 0)
does the trick.
You can also reproduce this by making the base memory allocator memset() the memory to random values.
[1] 48 is not including the Py_GC_Head header, which adds another 24 or 32 bytes depending on alignment requirements.