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 08447b5

Browse filesBrowse files
authored
gh-46376: Return existing pointer when possible in ctypes (#107131)
1 parent 68f9471 commit 08447b5
Copy full SHA for 08447b5

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/test/test_ctypes/test_keeprefs.py

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

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

102129
class PointerToStructure(unittest.TestCase):
103130
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
@@ -5129,6 +5129,8 @@ static PyObject *
51295129
Pointer_get_contents(CDataObject *self, void *closure)
51305130
{
51315131
StgDictObject *stgdict;
5132+
PyObject *keep, *ptr_probe;
5133+
CDataObject *ptr2ptr;
51325134

51335135
if (*(void **)self->b_ptr == NULL) {
51345136
PyErr_SetString(PyExc_ValueError,
@@ -5138,6 +5140,33 @@ Pointer_get_contents(CDataObject *self, void *closure)
51385140

51395141
stgdict = PyObject_stgdict((PyObject *)self);
51405142
assert(stgdict); /* Cannot be NULL for pointer instances */
5143+
5144+
keep = GetKeepedObjects(self);
5145+
if (keep != NULL) {
5146+
// check if it's a pointer to a pointer:
5147+
// pointers will have '0' key in the _objects
5148+
ptr_probe = PyDict_GetItemString(keep, "0");
5149+
5150+
if (ptr_probe != NULL) {
5151+
ptr2ptr = (CDataObject*) PyDict_GetItemString(keep, "1");
5152+
if (ptr2ptr == NULL) {
5153+
PyErr_SetString(PyExc_ValueError,
5154+
"Unexpected NULL pointer in _objects");
5155+
return NULL;
5156+
}
5157+
// don't construct a new object,
5158+
// return existing one instead to preserve refcount
5159+
assert(
5160+
*(void**) self->b_ptr == ptr2ptr->b_ptr ||
5161+
*(void**) self->b_value.c == ptr2ptr->b_ptr ||
5162+
*(void**) self->b_ptr == ptr2ptr->b_value.c ||
5163+
*(void**) self->b_value.c == ptr2ptr->b_value.c
5164+
); // double-check that we are returning the same thing
5165+
Py_INCREF(ptr2ptr);
5166+
return (PyObject *) ptr2ptr;
5167+
}
5168+
}
5169+
51415170
return PyCData_FromBaseObj(stgdict->proto,
51425171
(PyObject *)self, 0,
51435172
*(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.