]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-124538: Fix crash when using `gc.get_referents` on an untracked capsule object...
authorPeter Bierma <zintensitydev@gmail.com>
Thu, 26 Sep 2024 10:29:43 +0000 (06:29 -0400)
committerGitHub <noreply@github.com>
Thu, 26 Sep 2024 10:29:43 +0000 (12:29 +0200)
Lib/test/test_gc.py
Misc/NEWS.d/next/Library/2024-09-25-18-34-48.gh-issue-124538.nXZk4R.rst [new file with mode: 0644]
Objects/capsule.c

index 906f9884d6792feca48cecd4061cf079821f2326..bb7df1f5cfa7f78fd00917087dda5ff574a6a270 100644 (file)
@@ -1048,6 +1048,24 @@ class GCTests(unittest.TestCase):
         callback.assert_not_called()
         gc.enable()
 
+    @cpython_only
+    def test_get_referents_on_capsule(self):
+        # gh-124538: Calling gc.get_referents() on an untracked capsule must not crash.
+        import _datetime
+        import _socket
+        untracked_capsule = _datetime.datetime_CAPI
+        tracked_capsule = _socket.CAPI
+
+        # For whoever sees this in the future: if this is failing
+        # after making datetime's capsule tracked, that's fine -- this isn't something
+        # users are relying on. Just find a different capsule that is untracked.
+        self.assertFalse(gc.is_tracked(untracked_capsule))
+        self.assertTrue(gc.is_tracked(tracked_capsule))
+
+        self.assertEqual(len(gc.get_referents(untracked_capsule)), 0)
+        gc.get_referents(tracked_capsule)
+
+
 
 class IncrementalGCTests(unittest.TestCase):
 
diff --git a/Misc/NEWS.d/next/Library/2024-09-25-18-34-48.gh-issue-124538.nXZk4R.rst b/Misc/NEWS.d/next/Library/2024-09-25-18-34-48.gh-issue-124538.nXZk4R.rst
new file mode 100644 (file)
index 0000000..33ae037
--- /dev/null
@@ -0,0 +1 @@
+Fixed crash when using :func:`gc.get_referents` on a capsule object.
index 555979dab2b7893bb1b814c1e08a51110049f3d4..28965e0f21b7a07fa96cfa95c025ee3fbfe02f32 100644 (file)
@@ -317,10 +317,14 @@ static int
 capsule_traverse(PyCapsule *capsule, visitproc visit, void *arg)
 {
     // Capsule object is only tracked by the GC
-    // if _PyCapsule_SetTraverse() is called
-    assert(capsule->traverse_func != NULL);
+    // if _PyCapsule_SetTraverse() is called, but
+    // this can still be manually triggered by gc.get_referents()
+
+    if (capsule->traverse_func != NULL) {
+        return capsule->traverse_func((PyObject*)capsule, visit, arg);
+    }
 
-    return capsule->traverse_func((PyObject*)capsule, visit, arg);
+    return 0;
 }