gh-108240: _PyCapsule_SetTraverse() rejects NULL callbacks (#108417)

This commit is contained in:
Victor Stinner 2023-08-24 16:37:59 +02:00 committed by GitHub
parent 6726626646
commit a35d48d4bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 22 additions and 15 deletions

View File

@ -1,6 +1,9 @@
/* Wrap void * pointers to be passed between C modules */
#include "Python.h"
#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
#include "pycore_object.h" // _PyObject_GC_TRACK()
/* Internal structure of PyCapsule */
typedef struct {
@ -71,7 +74,7 @@ PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor)
capsule->destructor = destructor;
capsule->traverse_func = NULL;
capsule->clear_func = NULL;
// Only track the capsule if _PyCapsule_SetTraverse() is called
// Only track the object by the GC when _PyCapsule_SetTraverse() is called
return (PyObject *)capsule;
}
@ -204,8 +207,14 @@ _PyCapsule_SetTraverse(PyObject *op, traverseproc traverse_func, inquiry clear_f
}
PyCapsule *capsule = (PyCapsule *)op;
if (!PyObject_GC_IsTracked(op)) {
PyObject_GC_Track(op);
if (traverse_func == NULL || clear_func == NULL) {
PyErr_SetString(PyExc_ValueError,
"_PyCapsule_SetTraverse() called with NULL callback");
return -1;
}
if (!_PyObject_GC_IS_TRACKED(op)) {
_PyObject_GC_TRACK(op);
}
capsule->traverse_func = traverse_func;
@ -306,24 +315,22 @@ capsule_repr(PyObject *o)
static int
capsule_traverse(PyCapsule *capsule, visitproc visit, void *arg)
{
if (capsule->traverse_func) {
return capsule->traverse_func((PyObject*)capsule, visit, arg);
}
else {
return 0;
}
// Capsule object is only tracked by the GC
// if _PyCapsule_SetTraverse() is called
assert(capsule->traverse_func != NULL);
return capsule->traverse_func((PyObject*)capsule, visit, arg);
}
static int
capsule_clear(PyCapsule *capsule)
{
if (capsule->clear_func) {
return capsule->clear_func((PyObject*)capsule);
}
else {
return 0;
}
// Capsule object is only tracked by the GC
// if _PyCapsule_SetTraverse() is called
assert(capsule->clear_func != NULL);
return capsule->clear_func((PyObject*)capsule);
}