Skip to content

Commit 22dee7b

Browse files
[3.13] gh-124538: Fix crash when using gc.get_referents on an untracked capsule object (GH-124559) (#124588)
gh-124538: Fix crash when using `gc.get_referents` on an untracked capsule object (GH-124559) (cherry picked from commit f923605) Co-authored-by: Peter Bierma <[email protected]>
1 parent 6328446 commit 22dee7b

File tree

3 files changed

+26
-3
lines changed

3 files changed

+26
-3
lines changed

Lib/test/test_gc.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,24 @@ class Z:
10481048
callback.assert_not_called()
10491049
gc.enable()
10501050

1051+
@cpython_only
1052+
def test_get_referents_on_capsule(self):
1053+
# gh-124538: Calling gc.get_referents() on an untracked capsule must not crash.
1054+
import _datetime
1055+
import _socket
1056+
untracked_capsule = _datetime.datetime_CAPI
1057+
tracked_capsule = _socket.CAPI
1058+
1059+
# For whoever sees this in the future: if this is failing
1060+
# after making datetime's capsule tracked, that's fine -- this isn't something
1061+
# users are relying on. Just find a different capsule that is untracked.
1062+
self.assertFalse(gc.is_tracked(untracked_capsule))
1063+
self.assertTrue(gc.is_tracked(tracked_capsule))
1064+
1065+
self.assertEqual(len(gc.get_referents(untracked_capsule)), 0)
1066+
gc.get_referents(tracked_capsule)
1067+
1068+
10511069

10521070
class IncrementalGCTests(unittest.TestCase):
10531071

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed crash when using :func:`gc.get_referents` on a capsule object.

Objects/capsule.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,10 +317,14 @@ static int
317317
capsule_traverse(PyCapsule *capsule, visitproc visit, void *arg)
318318
{
319319
// Capsule object is only tracked by the GC
320-
// if _PyCapsule_SetTraverse() is called
321-
assert(capsule->traverse_func != NULL);
320+
// if _PyCapsule_SetTraverse() is called, but
321+
// this can still be manually triggered by gc.get_referents()
322+
323+
if (capsule->traverse_func != NULL) {
324+
return capsule->traverse_func((PyObject*)capsule, visit, arg);
325+
}
322326

323-
return capsule->traverse_func((PyObject*)capsule, visit, arg);
327+
return 0;
324328
}
325329

326330

0 commit comments

Comments
 (0)