WM: Fix invalid memory access in wmTimer handling code. 2.93 version #105406

Merged
Philipp Oeser merged 1 commits from mont29/blender:F-2.93-wmtimer-fix into blender-v2.93-release 2023-03-16 12:38:35 +01:00
4 changed files with 56 additions and 18 deletions
Showing only changes of commit e149018cd4 - Show all commits

View File

@ -361,6 +361,8 @@ struct wmTimer *WM_event_add_timer_notifier(struct wmWindowManager *wm,
struct wmWindow *win,
unsigned int type,
double timestep);
/** Mark the given `timer` to be removed, actual removal and deletion is deferred and handled
* internally by the window manager code. */
void WM_event_remove_timer(struct wmWindowManager *wm,
struct wmWindow *win,
struct wmTimer *timer);

View File

@ -684,6 +684,11 @@ typedef struct wmNDOFMotionData {
typedef enum {
/** Do not attempt to free customdata pointer even if non-NULL. */
WM_TIMER_NO_FREE_CUSTOM_DATA = 1 << 0,
/* Internal falgs, should not be used outside of WM code. */
/** This timer has been tagged for removal and deletion, handled by WM code to ensure timers are
* deleted in a safe context. */
WM_TIMER_TAGGED_FOR_REMOVAL = 1 << 16,
} wmTimerFlags;
typedef struct wmTimer {

View File

@ -215,6 +215,9 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
/* end running jobs, a job end also removes its timer */
LISTBASE_FOREACH_MUTABLE (wmTimer *, wt, &wm->timers) {
if (wt->flags & WM_TIMER_TAGGED_FOR_REMOVAL) {
continue;
}
if (wt->win == win && wt->event_type == TIMERJOBS) {
wm_jobs_timer_end(wm, wt);
}
@ -222,10 +225,14 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
/* timer removing, need to call this api function */
LISTBASE_FOREACH_MUTABLE (wmTimer *, wt, &wm->timers) {
if (wt->flags & WM_TIMER_TAGGED_FOR_REMOVAL) {
continue;
}
if (wt->win == win) {
WM_event_remove_timer(wm, win, wt);
}
}
wm_window_delete_removed_timers(wm);
if (win->eventstate) {
MEM_freeN(win->eventstate);
@ -1507,6 +1514,9 @@ static int wm_window_timer(const bContext *C)
/* Mutable in case the timer gets removed. */
LISTBASE_FOREACH_MUTABLE (wmTimer *, wt, &wm->timers) {
if (wt->flags & WM_TIMER_TAGGED_FOR_REMOVAL) {
continue;
}
wmWindow *win = wt->win;
if (wt->sleep != 0) {
@ -1544,6 +1554,10 @@ static int wm_window_timer(const bContext *C)
}
}
}
/* Effectively delete all timers marked for removal. */
wm_window_delete_removed_timers(wm);
return retval;
}
@ -1622,6 +1636,9 @@ void WM_event_timer_sleep(wmWindowManager *wm,
bool do_sleep)
{
LISTBASE_FOREACH (wmTimer *, wt, &wm->timers) {
if (wt->flags & WM_TIMER_TAGGED_FOR_REMOVAL) {
continue;
}
if (wt == timer) {
wt->sleep = do_sleep;
break;
@ -1666,38 +1683,48 @@ wmTimer *WM_event_add_timer_notifier(wmWindowManager *wm,
return wt;
}
void wm_window_delete_removed_timers(wmWindowManager *wm)
{
LISTBASE_FOREACH_MUTABLE (wmTimer *, wt, &wm->timers) {
if ((wt->flags & WM_TIMER_TAGGED_FOR_REMOVAL) == 0) {
continue;
}
/* Actual removal and freeing of the timer. */
BLI_remlink(&wm->timers, wt);
MEM_freeN(wt);
}
}
void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
{
/* extra security check */
wmTimer *wt = NULL;
LISTBASE_FOREACH (wmTimer *, timer_iter, &wm->timers) {
if (timer_iter == timer) {
wt = timer_iter;
}
}
if (wt == NULL) {
/* Extra security check. */
if (BLI_findindex(&wm->timers, timer) < 0) {
return;
}
if (wm->reports.reporttimer == wt) {
timer->flags |= WM_TIMER_TAGGED_FOR_REMOVAL;
/* Clear existing references to the timer. */
if (wm->reports.reporttimer == timer) {
wm->reports.reporttimer = NULL;
}
BLI_remlink(&wm->timers, wt);
if (wt->customdata != NULL && (wt->flags & WM_TIMER_NO_FREE_CUSTOM_DATA) == 0) {
MEM_freeN(wt->customdata);
}
MEM_freeN(wt);
/* there might be events in queue with this timer as customdata */
/* There might be events in queue with this timer as customdata. */
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
LISTBASE_FOREACH (wmEvent *, event, &win->event_queue) {
if (event->customdata == wt) {
if (event->customdata == timer) {
event->customdata = NULL;
event->type = EVENT_NONE; /* Timer users customdata, don't want `NULL == NULL`. */
}
}
}
/* Immediately free customdata if requested, so that invalid usages of that data after
* calling `WM_event_remove_timer` can be easily spotted (through ASAN errors e.g.). */
if (timer->customdata != NULL && (timer->flags & WM_TIMER_NO_FREE_CUSTOM_DATA) == 0) {
MEM_freeN(timer->customdata);
timer->customdata = NULL;
}
}
void WM_event_remove_timer_notifier(wmWindowManager *wm, wmWindow *win, wmTimer *timer)

View File

@ -78,6 +78,10 @@ void wm_window_IME_begin(wmWindow *win, int x, int y, int w, int h, bool complet
void wm_window_IME_end(wmWindow *win);
#endif
/** Effectively remove timers from the list and delete them. Calling this should only be done by
* internal WM management code, from specific, safe places. */
void wm_window_delete_removed_timers(wmWindowManager *wm);
/* *************** window operators ************** */
int wm_window_close_exec(bContext *C, struct wmOperator *op);
int wm_window_fullscreen_toggle_exec(bContext *C, struct wmOperator *op);