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_EventBackendX11;
|
||||||
extern const W_EventBackend W_EventBackendWL;
|
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_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,
|
void W_WL_SetLoopDispatch(struct W_Screen *scr,
|
||||||
int loop_fd,
|
int loop_fd,
|
||||||
void (*dispatch_fn)(int timeout_ms, void *ud),
|
void (*dispatch_fn)(int timeout_ms, void *ud),
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,9 @@ int WMHandleEvent(WMEvent * event)
|
||||||
return False;
|
return False;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!view->screen || view->flags.alreadyDead) /* view was destroyed between queue and dispatch */
|
||||||
|
return True;
|
||||||
|
|
||||||
view->screen->lastEventTime = getEventTime(view->screen, event);
|
view->screen->lastEventTime = getEventTime(view->screen, event);
|
||||||
|
|
||||||
toplevel = W_TopLevelOfView(view);
|
toplevel = W_TopLevelOfView(view);
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,8 @@ wl_fetch(void *ectx, WMEvent *ev)
|
||||||
WLQEntry *e = &ctx->buf[ctx->head];
|
WLQEntry *e = &ctx->buf[ctx->head];
|
||||||
*ev = e->ev;
|
*ev = e->ev;
|
||||||
ev->_view_target = e->target;
|
ev->_view_target = e->target;
|
||||||
|
if (e->target && !W_IsValidView(e->target))
|
||||||
|
ev->_view_target = NULL;
|
||||||
if (e->target) {
|
if (e->target) {
|
||||||
W_Screen *scr = e->target->screen;
|
W_Screen *scr = e->target->screen;
|
||||||
if (scr)
|
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);
|
unsigned long emask = wme_mask(ctx->buf[i].ev.type);
|
||||||
if (emask & (unsigned long)mask) {
|
if (emask & (unsigned long)mask) {
|
||||||
*ev = ctx->buf[i].ev;
|
*ev = ctx->buf[i].ev;
|
||||||
|
if (ev->_view_target && !W_IsValidView(ev->_view_target))
|
||||||
|
ev->_view_target = NULL;
|
||||||
/* Remove entry by shifting */
|
/* Remove entry by shifting */
|
||||||
int next = (i + 1) % WL_EVENT_QUEUE_CAP;
|
int next = (i + 1) % WL_EVENT_QUEUE_CAP;
|
||||||
while (next != ctx->tail) {
|
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 &&
|
if (ctx->buf[i].ev.type == type &&
|
||||||
ctx->buf[i].ev.window == w) {
|
ctx->buf[i].ev.window == w) {
|
||||||
*ev = ctx->buf[i].ev;
|
*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;
|
int next = (i + 1) % WL_EVENT_QUEUE_CAP;
|
||||||
while (next != ctx->tail) {
|
while (next != ctx->tail) {
|
||||||
ctx->buf[i] = ctx->buf[next];
|
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). */
|
/* 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
|
void
|
||||||
W_WL_DispatchLoop(W_Screen *scr, int timeout_ms)
|
W_WL_DispatchLoop(W_Screen *scr, int timeout_ms)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,8 @@ static void destroyView(W_View * view)
|
||||||
if (view->backColor)
|
if (view->backColor)
|
||||||
WMReleaseColor(view->backColor);
|
WMReleaseColor(view->backColor);
|
||||||
|
|
||||||
|
view->screen = NULL; /* invalidate for in-flight events */
|
||||||
|
|
||||||
wfree(view);
|
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
|
* 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
|
* 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)
|
static void wl_destroy(void *vctx, W_View *view)
|
||||||
{
|
{
|
||||||
(void)vctx;
|
(void)vctx;
|
||||||
(void)view;
|
W_WL_InvalidateView(view->screen, view);
|
||||||
/* Nothing to free — synthetic ID only */
|
/* Nothing to free — synthetic ID only */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue