gh-46376: Return existing pointer when possible in ctypes (#107131)

This commit is contained in:
Konstantin 2023-07-31 09:10:53 +02:00 committed by GitHub
parent 68f94715bb
commit 08447b5deb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 0 deletions

View File

@ -98,6 +98,33 @@ class PointerTestCase(unittest.TestCase):
x = pointer(i)
self.assertEqual(x._objects, {'1': i})
def test_pp_ownership(self):
d = c_int(123)
n = c_int(456)
p = pointer(d)
pp = pointer(p)
self.assertIs(pp._objects['1'], p)
self.assertIs(pp._objects['0']['1'], d)
pp.contents.contents = n
self.assertIs(pp._objects['1'], p)
self.assertIs(pp._objects['0']['1'], n)
self.assertIs(p._objects['1'], n)
self.assertEqual(len(p._objects), 1)
del d
del p
self.assertIs(pp._objects['0']['1'], n)
self.assertEqual(len(pp._objects), 2)
del n
self.assertEqual(len(pp._objects), 2)
class PointerToStructure(unittest.TestCase):
def test(self):

View File

@ -0,0 +1 @@
Prevent memory leak and use-after-free when using pointers to pointers with ctypes

View File

@ -5129,6 +5129,8 @@ static PyObject *
Pointer_get_contents(CDataObject *self, void *closure)
{
StgDictObject *stgdict;
PyObject *keep, *ptr_probe;
CDataObject *ptr2ptr;
if (*(void **)self->b_ptr == NULL) {
PyErr_SetString(PyExc_ValueError,
@ -5138,6 +5140,33 @@ Pointer_get_contents(CDataObject *self, void *closure)
stgdict = PyObject_stgdict((PyObject *)self);
assert(stgdict); /* Cannot be NULL for pointer instances */
keep = GetKeepedObjects(self);
if (keep != NULL) {
// check if it's a pointer to a pointer:
// pointers will have '0' key in the _objects
ptr_probe = PyDict_GetItemString(keep, "0");
if (ptr_probe != NULL) {
ptr2ptr = (CDataObject*) PyDict_GetItemString(keep, "1");
if (ptr2ptr == NULL) {
PyErr_SetString(PyExc_ValueError,
"Unexpected NULL pointer in _objects");
return NULL;
}
// don't construct a new object,
// return existing one instead to preserve refcount
assert(
*(void**) self->b_ptr == ptr2ptr->b_ptr ||
*(void**) self->b_value.c == ptr2ptr->b_ptr ||
*(void**) self->b_ptr == ptr2ptr->b_value.c ||
*(void**) self->b_value.c == ptr2ptr->b_value.c
); // double-check that we are returning the same thing
Py_INCREF(ptr2ptr);
return (PyObject *) ptr2ptr;
}
}
return PyCData_FromBaseObj(stgdict->proto,
(PyObject *)self, 0,
*(void **)self->b_ptr);