mirror of
https://github.com/python/cpython.git
synced 2025-09-27 10:50:04 +00:00
[3.12] gh-46376: Return existing pointer when possible in ctypes (GH-107131) (#107487)
(cherry picked from commit 08447b5deb
)
Co-authored-by: Konstantin <kpp.live+github@gmail.com>
This commit is contained in:
parent
04bd8c76b2
commit
54aaaadef8
3 changed files with 57 additions and 0 deletions
|
@ -93,6 +93,33 @@ class PointerTestCase(unittest.TestCase):
|
||||||
x = pointer(i)
|
x = pointer(i)
|
||||||
self.assertEqual(x._objects, {'1': 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):
|
class PointerToStructure(unittest.TestCase):
|
||||||
def test(self):
|
def test(self):
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Prevent memory leak and use-after-free when using pointers to pointers with ctypes
|
|
@ -5122,6 +5122,8 @@ static PyObject *
|
||||||
Pointer_get_contents(CDataObject *self, void *closure)
|
Pointer_get_contents(CDataObject *self, void *closure)
|
||||||
{
|
{
|
||||||
StgDictObject *stgdict;
|
StgDictObject *stgdict;
|
||||||
|
PyObject *keep, *ptr_probe;
|
||||||
|
CDataObject *ptr2ptr;
|
||||||
|
|
||||||
if (*(void **)self->b_ptr == NULL) {
|
if (*(void **)self->b_ptr == NULL) {
|
||||||
PyErr_SetString(PyExc_ValueError,
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
@ -5131,6 +5133,33 @@ Pointer_get_contents(CDataObject *self, void *closure)
|
||||||
|
|
||||||
stgdict = PyObject_stgdict((PyObject *)self);
|
stgdict = PyObject_stgdict((PyObject *)self);
|
||||||
assert(stgdict); /* Cannot be NULL for pointer instances */
|
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,
|
return PyCData_FromBaseObj(stgdict->proto,
|
||||||
(PyObject *)self, 0,
|
(PyObject *)self, 0,
|
||||||
*(void **)self->b_ptr);
|
*(void **)self->b_ptr);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue