From f24854005d8688fb86cf41bd62a63580c63f818d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 7 Jan 2022 16:32:01 +1100 Subject: Fix T94708: negative reference count error with Python API callbacks Regression in 7972785d7b90771f50534fe3e1101d8adb615fa3 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. --- source/blender/editors/include/ED_space_api.h | 2 +- source/blender/editors/space_api/spacetypes.c | 5 +++-- source/blender/python/intern/bpy_rna_callback.c | 16 +++++++++++----- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/include/ED_space_api.h b/source/blender/editors/include/ED_space_api.h index f4a69737da1..fb76b36baef 100644 --- a/source/blender/editors/include/ED_space_api.h +++ b/source/blender/editors/include/ED_space_api.h @@ -86,7 +86,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 *)); diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 93d023c8bb4..f8adba30547 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -248,15 +248,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) diff --git a/source/blender/python/intern/bpy_rna_callback.c b/source/blender/python/intern/bpy_rna_callback.c index 16968cebab4..16ea4bf3e5e 100644 --- a/source/blender/python/intern/bpy_rna_callback.c +++ b/source/blender/python/intern/bpy_rna_callback.c @@ -383,6 +383,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"); @@ -406,7 +407,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)) { @@ -445,7 +446,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 { @@ -453,9 +454,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) { -- cgit v1.2.3