WM: Fix invalid memory access in wmTimer handling code. 2.93 version #105406
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue