Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 57f27e4

Browse filesBrowse files
ambvcode-of-kpp
andauthored
[3.11] gh-46376: Return existing pointer when possible in ctypes (GH-107131) (#107488)
(cherry picked from commit 08447b5) Co-authored-by: Konstantin <kpp.live+github@gmail.com>
1 parent aa5f2b1 commit 57f27e4
Copy full SHA for 57f27e4

File tree

Expand file treeCollapse file tree

3 files changed

+57
-0
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+57
-0
lines changed

‎Lib/ctypes/test/test_keeprefs.py

Copy file name to clipboardExpand all lines: Lib/ctypes/test/test_keeprefs.py
+27Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,33 @@ def test_p_cint(self):
9393
x = pointer(i)
9494
self.assertEqual(x._objects, {'1': i})
9595

96+
def test_pp_ownership(self):
97+
d = c_int(123)
98+
n = c_int(456)
99+
100+
p = pointer(d)
101+
pp = pointer(p)
102+
103+
self.assertIs(pp._objects['1'], p)
104+
self.assertIs(pp._objects['0']['1'], d)
105+
106+
pp.contents.contents = n
107+
108+
self.assertIs(pp._objects['1'], p)
109+
self.assertIs(pp._objects['0']['1'], n)
110+
111+
self.assertIs(p._objects['1'], n)
112+
self.assertEqual(len(p._objects), 1)
113+
114+
del d
115+
del p
116+
117+
self.assertIs(pp._objects['0']['1'], n)
118+
self.assertEqual(len(pp._objects), 2)
119+
120+
del n
121+
122+
self.assertEqual(len(pp._objects), 2)
96123

97124
class PointerToStructure(unittest.TestCase):
98125
def test(self):
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Prevent memory leak and use-after-free when using pointers to pointers with ctypes

‎Modules/_ctypes/_ctypes.c

Copy file name to clipboardExpand all lines: Modules/_ctypes/_ctypes.c
+29Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5158,6 +5158,8 @@ static PyObject *
51585158
Pointer_get_contents(CDataObject *self, void *closure)
51595159
{
51605160
StgDictObject *stgdict;
5161+
PyObject *keep, *ptr_probe;
5162+
CDataObject *ptr2ptr;
51615163

51625164
if (*(void **)self->b_ptr == NULL) {
51635165
PyErr_SetString(PyExc_ValueError,
@@ -5167,6 +5169,33 @@ Pointer_get_contents(CDataObject *self, void *closure)
51675169

51685170
stgdict = PyObject_stgdict((PyObject *)self);
51695171
assert(stgdict); /* Cannot be NULL for pointer instances */
5172+
5173+
keep = GetKeepedObjects(self);
5174+
if (keep != NULL) {
5175+
// check if it's a pointer to a pointer:
5176+
// pointers will have '0' key in the _objects
5177+
ptr_probe = PyDict_GetItemString(keep, "0");
5178+
5179+
if (ptr_probe != NULL) {
5180+
ptr2ptr = (CDataObject*) PyDict_GetItemString(keep, "1");
5181+
if (ptr2ptr == NULL) {
5182+
PyErr_SetString(PyExc_ValueError,
5183+
"Unexpected NULL pointer in _objects");
5184+
return NULL;
5185+
}
5186+
// don't construct a new object,
5187+
// return existing one instead to preserve refcount
5188+
assert(
5189+
*(void**) self->b_ptr == ptr2ptr->b_ptr ||
5190+
*(void**) self->b_value.c == ptr2ptr->b_ptr ||
5191+
*(void**) self->b_ptr == ptr2ptr->b_value.c ||
5192+
*(void**) self->b_value.c == ptr2ptr->b_value.c
5193+
); // double-check that we are returning the same thing
5194+
Py_INCREF(ptr2ptr);
5195+
return (PyObject *) ptr2ptr;
5196+
}
5197+
}
5198+
51705199
return PyCData_FromBaseObj(stgdict->proto,
51715200
(PyObject *)self, 0,
51725201
*(void **)self->b_ptr);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.