Fix T94708: negative reference count error with Python API callbacks

Regression in 7972785d7b that caused
Python callback arguments to be de-referenced twice - potentially
accessing freed memory. Making a new-file with a circle-select
tool active triggered this (for example).

Now arguments aren't de-referenced when Blender it's self has already
removed the callback handle.
This commit is contained in:
Campbell Barton 2022-01-07 16:32:01 +11:00 committed by Philipp Oeser
parent 4c8740a452
commit 3b686b8233
Notes: blender-bot 2023-02-14 06:32:27 +01:00
Referenced by issue #94708, New-file causes Python assert with negative reference count
Referenced by issue #93479, 3.0 Potential candidates for corrective releases
3 changed files with 15 additions and 8 deletions

View File

@ -74,7 +74,7 @@ void *ED_region_draw_cb_activate(struct ARegionType *art,
int type);
void ED_region_draw_cb_draw(const struct bContext *C, struct ARegion *region, int type);
void ED_region_surface_draw_cb_draw(struct ARegionType *art, int type);
void ED_region_draw_cb_exit(struct ARegionType *art, void *handle);
bool ED_region_draw_cb_exit(struct ARegionType *art, void *handle);
void ED_region_draw_cb_remove_by_type(struct ARegionType *art,
void *draw_fn,
void (*free)(void *));

View File

@ -253,15 +253,16 @@ void *ED_region_draw_cb_activate(ARegionType *art,
return rdc;
}
void ED_region_draw_cb_exit(ARegionType *art, void *handle)
bool ED_region_draw_cb_exit(ARegionType *art, void *handle)
{
LISTBASE_FOREACH (RegionDrawCB *, rdc, &art->drawcalls) {
if (rdc == (RegionDrawCB *)handle) {
BLI_remlink(&art->drawcalls, rdc);
MEM_freeN(rdc);
return;
return true;
}
}
return false;
}
static void ed_region_draw_cb_draw(const bContext *C, ARegion *region, ARegionType *art, int type)

View File

@ -380,6 +380,7 @@ PyObject *pyrna_callback_classmethod_remove(PyObject *UNUSED(self), PyObject *ar
void *handle;
StructRNA *srna;
bool capsule_clear = false;
bool handle_removed = false;
if (PyTuple_GET_SIZE(args) < 2) {
PyErr_SetString(PyExc_ValueError, "callback_remove(handler): expected at least 2 args");
@ -403,7 +404,7 @@ PyObject *pyrna_callback_classmethod_remove(PyObject *UNUSED(self), PyObject *ar
args, "OO!:WindowManager.draw_cursor_remove", &cls, &PyCapsule_Type, &py_handle)) {
return NULL;
}
WM_paint_cursor_end(handle);
handle_removed = WM_paint_cursor_end(handle);
capsule_clear = true;
}
else if (RNA_struct_is_a(srna, &RNA_Space)) {
@ -442,7 +443,7 @@ PyObject *pyrna_callback_classmethod_remove(PyObject *UNUSED(self), PyObject *ar
params.region_type_enum.value_orig);
return NULL;
}
ED_region_draw_cb_exit(art, handle);
handle_removed = ED_region_draw_cb_exit(art, handle);
capsule_clear = true;
}
else {
@ -450,9 +451,14 @@ PyObject *pyrna_callback_classmethod_remove(PyObject *UNUSED(self), PyObject *ar
return NULL;
}
/* The handle has been removed, so decrement its customdata. */
PyObject *handle_args = PyCapsule_GetContext(py_handle);
Py_DECREF(handle_args);
/* When `handle_removed == false`: Blender has already freed the data
* (freeing screen data when loading a new file for example).
* This will have already decremented the user, so don't decrement twice. */
if (handle_removed == true) {
/* The handle has been removed, so decrement its custom-data. */
PyObject *handle_args = PyCapsule_GetContext(py_handle);
Py_DECREF(handle_args);
}
/* don't allow reuse */
if (capsule_clear) {