Seen in test_multiprocessing: ==31662== 44 bytes in 1 blocks are definitely lost in loss record 687 of 10,548 ==31662== at 0x4C2154B: malloc (vg_replace_malloc.c:236) ==31662== by 0x41CC27: PyMem_Malloc (object.c:1699) ==31662== by 0x127D9F51: resize (callproc.c:1664) ==31662== by 0x5759D8: PyCFunction_Call (methodobject.c:81) ==31662== by 0x48E294: call_function (ceval.c:3980) ==31662== by 0x4895E1: PyEval_EvalFrameEx (ceval.c:2605) ==31662== by 0x48E67B: fast_function (ceval.c:4068) ==31662== by 0x48E3C7: call_function (ceval.c:4001) ==31662== by 0x4895E1: PyEval_EvalFrameEx (ceval.c:2605) ==31662== by 0x48C54F: PyEval_EvalCodeEx (ceval.c:3355) ==31662== by 0x48E786: fast_function (ceval.c:4078) ==31662== by 0x48E3C7: call_function (ceval.c:4001)
How did you obtain this? the resize() function is not called by test_multiprocessing. And are you sure that it's not some kind of reference leak? (this pointer is tied to a CDataObject; its tp_alloc should free the memory)
I can reproduce this with: valgrind --tool=memcheck --log-file=leaks.txt --leak-check=full --suppressions=Misc/valgrind-python.supp ./python -m test test_ctypes Where as: valgrind --tool=memcheck --log-file=leaks.txt --leak-check=full --suppressions=Misc/valgrind-python.supp ./python -m test test_multiprocessing turns up nothing in 'ctypes.resize'.
> this pointer is tied to a CDataObject; its tp_alloc should free the > memory The free in 'PyCData_clear' is conditional: if ((self->b_needsfree) && ((size_t)dict->size > sizeof(self->b_value))) PyMem_Free(self->b_ptr); As written, 'PyCData_clear' has no way of knowing that memory has been {m,re}alloc'd in 'resize'. So in some cases memory will leak. Here is a small reproduction case extracted from 'test_varsize_struct.py'. from ctypes import * class X(Structure): _fields_ = [("item", c_int), ("array", c_int * 1)] x = X() x.item = 42 x.array[0] = 100 new_size = sizeof(X) + sizeof(c_int) * 5 resize(x, new_size) One potential fix is: diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2440,7 +2440,7 @@ PyCData_clear(CDataObject *self) assert(dict); /* Cannot be NULL for CDataObject instances */ Py_CLEAR(self->b_objects); if ((self->b_needsfree) - && ((size_t)dict->size > sizeof(self->b_value))) + && (self->b_ptr != (char *)&self->b_value)) PyMem_Free(self->b_ptr); self->b_ptr = NULL; Py_CLEAR(self->b_base); I need to think about that more, though.