fix: eliminate use-after-free crash on modal dialog close
- wevent_wl.c: W_IsValidView() validates _view_target against view_table
at fetch boundary (wl_fetch, wl_check_mask, wl_check_typed_window);
nulls target before returning event if view was destroyed
- wevent_wl.c: W_WL_InvalidateView() nulls queue entries at destroy time
- wview.c: set view->screen=NULL before wfree(view)
- wevent.c: defense-in-depth: check !view->screen || alreadyDead before use
This commit is contained in:
parent
9943c05b4a
commit
15bec04b1b
|
|
@ -480,7 +480,10 @@ typedef struct W_EventBackend {
|
|||
extern const W_EventBackend W_EventBackendX11;
|
||||
extern const W_EventBackend W_EventBackendWL;
|
||||
|
||||
Bool W_IsValidView(struct W_View *view);
|
||||
|
||||
void W_WL_EnqueueEvent(struct W_Screen *scr, const WMEvent *ev, struct W_View *target);
|
||||
void W_WL_InvalidateView(struct W_Screen *scr, struct W_View *target);
|
||||
void W_WL_SetLoopDispatch(struct W_Screen *scr,
|
||||
int loop_fd,
|
||||
void (*dispatch_fn)(int timeout_ms, void *ud),
|
||||
|
|
|
|||
|
|
@ -206,6 +206,9 @@ int WMHandleEvent(WMEvent * event)
|
|||
return False;
|
||||
}
|
||||
|
||||
if (!view->screen || view->flags.alreadyDead) /* view was destroyed between queue and dispatch */
|
||||
return True;
|
||||
|
||||
view->screen->lastEventTime = getEventTime(view->screen, event);
|
||||
|
||||
toplevel = W_TopLevelOfView(view);
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ wl_fetch(void *ectx, WMEvent *ev)
|
|||
WLQEntry *e = &ctx->buf[ctx->head];
|
||||
*ev = e->ev;
|
||||
ev->_view_target = e->target;
|
||||
if (e->target && !W_IsValidView(e->target))
|
||||
ev->_view_target = NULL;
|
||||
if (e->target) {
|
||||
W_Screen *scr = e->target->screen;
|
||||
if (scr)
|
||||
|
|
@ -110,6 +112,8 @@ wl_check_mask(void *ectx, long mask, WMEvent *ev)
|
|||
unsigned long emask = wme_mask(ctx->buf[i].ev.type);
|
||||
if (emask & (unsigned long)mask) {
|
||||
*ev = ctx->buf[i].ev;
|
||||
if (ev->_view_target && !W_IsValidView(ev->_view_target))
|
||||
ev->_view_target = NULL;
|
||||
/* Remove entry by shifting */
|
||||
int next = (i + 1) % WL_EVENT_QUEUE_CAP;
|
||||
while (next != ctx->tail) {
|
||||
|
|
@ -134,6 +138,8 @@ wl_check_typed_window(void *ectx, WNativeWindow w, int type, WMEvent *ev)
|
|||
if (ctx->buf[i].ev.type == type &&
|
||||
ctx->buf[i].ev.window == w) {
|
||||
*ev = ctx->buf[i].ev;
|
||||
if (ev->_view_target && !W_IsValidView(ev->_view_target))
|
||||
ev->_view_target = NULL;
|
||||
int next = (i + 1) % WL_EVENT_QUEUE_CAP;
|
||||
while (next != ctx->tail) {
|
||||
ctx->buf[i] = ctx->buf[next];
|
||||
|
|
@ -224,6 +230,21 @@ W_WL_SetLoopDispatch(W_Screen *scr,
|
|||
}
|
||||
|
||||
/* Tick the Wayland compositor event loop once (used by modal loops). */
|
||||
void
|
||||
W_WL_InvalidateView(W_Screen *scr, W_View *target)
|
||||
{
|
||||
if (!scr || !scr->eventBackendCtx)
|
||||
return;
|
||||
WLEventCtx *ctx = scr->eventBackendCtx;
|
||||
int i = ctx->head;
|
||||
while (i != ctx->tail) {
|
||||
if (ctx->buf[i].target == target) {
|
||||
ctx->buf[i].target = NULL;
|
||||
}
|
||||
i = (i + 1) % WL_EVENT_QUEUE_CAP;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
W_WL_DispatchLoop(W_Screen *scr, int timeout_ms)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -374,6 +374,8 @@ static void destroyView(W_View * view)
|
|||
if (view->backColor)
|
||||
WMReleaseColor(view->backColor);
|
||||
|
||||
view->screen = NULL; /* invalidate for in-flight events */
|
||||
|
||||
wfree(view);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,20 @@ static void view_table_remove(Window id)
|
|||
}
|
||||
}
|
||||
|
||||
Bool W_IsValidView(W_View *view)
|
||||
{
|
||||
if (!view) return False;
|
||||
unsigned idx = ((unsigned)view->window) % VIEW_TABLE_SIZE;
|
||||
for (int i = 0; i < VIEW_TABLE_SIZE; i++) {
|
||||
unsigned slot = (idx + i) % VIEW_TABLE_SIZE;
|
||||
if (view_table[slot].view == view)
|
||||
return True;
|
||||
if (view_table[slot].id == 0)
|
||||
return False;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/*
|
||||
* W_RegisterBacking - register a pixman_image_t backing for any synthetic
|
||||
* window ID (WINGs views OR WM frame_buf windows). Used by wl_backend.c so
|
||||
|
|
@ -231,7 +245,7 @@ static void wl_noop_reparent(void *vctx, W_View *view, W_View *newParent, int x,
|
|||
static void wl_destroy(void *vctx, W_View *view)
|
||||
{
|
||||
(void)vctx;
|
||||
(void)view;
|
||||
W_WL_InvalidateView(view->screen, view);
|
||||
/* Nothing to free — synthetic ID only */
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue