wayland: NULL fb->scene_buf/tree when wlroots auto-destroys cascade
ASan caught a SEGV in wl_list_remove inside wlr_scene_node_destroy
called from wl_view_destroy_backing_impl when closing a dock-icon
rename input dialog.
The flow:
1. wInputDialog opens. fake_leader fb_FAKE created with tree+scene_buf;
panel content fb_PANEL created with scene_buf parented under
fb_FAKE's tree; wManageInternalWindow wraps fake_leader in a
WMaker frame fb_FRAME (also with tree).
2. Dialog closes. wUnmanageWindow runs wl_frame_destroy on fb_FRAME.
wlr_scene_node_destroy on fb_FRAME->tree cascades — wlroots
auto-destroys fb_FAKE->tree, fb_FAKE->scene_buf, AND
fb_PANEL->scene_buf.
3. WMDestroyInputPanel later calls destroyView →
wl_view_destroy_backing_impl(panel_id), which tries
wlr_scene_node_destroy on fb_PANEL->scene_buf — already destroyed
and freed. wl_list_remove on a zeroed link → SEGV.
Fix: register destroy listeners on every fb->scene_buf and fb->tree
when they're created. When wlroots destroys the node (typically via
parent-tree cascade), the listener fires and NULLs the field.
wl_view_destroy_backing_impl and wl_frame_destroy already check for
NULL before destroying, so the second-destroy is now safely skipped.
Same pattern as the earlier fix for view->scene_node (commit 8cb56fc),
applied to all wl_frame_buf scene-graph fields.
This commit is contained in:
parent
c55d42c169
commit
852fb2adeb
|
|
@ -300,6 +300,13 @@ struct wl_frame_buf {
|
|||
pixman_image_t *image; /* ARGB32 pixel data */
|
||||
struct wlr_scene_buffer *scene_buf; /* scene graph node */
|
||||
struct wlr_scene_tree *tree; /* grouping tree (top-level frames only) */
|
||||
/* Destroy listeners on scene_buf and tree so they get NULL'd when
|
||||
* wlroots auto-destroys them (e.g. via cascade from parent tree
|
||||
* destruction during wl_frame_destroy). Without these, code that
|
||||
* later tries to wlr_scene_node_destroy them again segfaults inside
|
||||
* wl_list_remove on already-zeroed list links. */
|
||||
struct wl_listener scene_buf_destroy;
|
||||
struct wl_listener tree_destroy;
|
||||
WNativeWindow id; /* synthetic ID (>= 0x10000000) */
|
||||
WNativeWindow parent_id; /* parent frame_buf ID (0 = root) */
|
||||
int x, y; /* position in output coords */
|
||||
|
|
@ -4023,6 +4030,46 @@ frame_buf_find(WNativeWindow id)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* fb_scene_buf_attach / fb_tree_attach
|
||||
*
|
||||
* Register destroy listeners on a scene_buf or tree so that fb->scene_buf
|
||||
* (or fb->tree) is NULL'd when wlroots auto-destroys the node — typically
|
||||
* via cascade when an ancestor scene_tree is destroyed. Without this,
|
||||
* follow-up cleanup paths (wl_view_destroy_backing_impl, wl_frame_destroy)
|
||||
* call wlr_scene_node_destroy on a freed node and segfault inside
|
||||
* wl_list_remove against a zeroed list link.
|
||||
*/
|
||||
static void fb_scene_buf_destroy_handler(struct wl_listener *l, void *d)
|
||||
{
|
||||
(void)d;
|
||||
struct wl_frame_buf *fb = wl_container_of(l, fb, scene_buf_destroy);
|
||||
fb->scene_buf = NULL;
|
||||
wl_list_remove(&fb->scene_buf_destroy.link);
|
||||
}
|
||||
static void fb_tree_destroy_handler(struct wl_listener *l, void *d)
|
||||
{
|
||||
(void)d;
|
||||
struct wl_frame_buf *fb = wl_container_of(l, fb, tree_destroy);
|
||||
fb->tree = NULL;
|
||||
/* The tree's destroy cascades into scene_buf — its destroy handler
|
||||
* will fire too and NULL fb->scene_buf separately. */
|
||||
wl_list_remove(&fb->tree_destroy.link);
|
||||
}
|
||||
static void fb_attach_scene_buf_listener(struct wl_frame_buf *fb)
|
||||
{
|
||||
if (!fb || !fb->scene_buf) return;
|
||||
fb->scene_buf_destroy.notify = fb_scene_buf_destroy_handler;
|
||||
wl_signal_add(&fb->scene_buf->node.events.destroy,
|
||||
&fb->scene_buf_destroy);
|
||||
}
|
||||
static void fb_attach_tree_listener(struct wl_frame_buf *fb)
|
||||
{
|
||||
if (!fb || !fb->tree) return;
|
||||
fb->tree_destroy.notify = fb_tree_destroy_handler;
|
||||
wl_signal_add(&fb->tree->node.events.destroy, &fb->tree_destroy);
|
||||
}
|
||||
|
||||
/* Recursively update fb->x, fb->y for all descendants of `parent`, used
|
||||
* to keep absolute-position tracking in sync after the parent moves.
|
||||
* Hit-testing (frame_buf_at, frame_buf_button_at) and click coordinate
|
||||
|
|
@ -4229,9 +4276,11 @@ wl_frame_create_toplevel(WScreen *scr,
|
|||
? wl_state.level_trees[WL_LAYER_NORMAL] : &wl_state.scene->tree;
|
||||
fb->tree = wlr_scene_tree_create(_parent);
|
||||
if (fb->tree) {
|
||||
fb_attach_tree_listener(fb);
|
||||
wlr_scene_node_set_position(&fb->tree->node, x, y);
|
||||
fb->scene_buf = wlr_scene_buffer_create(fb->tree, &fb->base);
|
||||
if (fb->scene_buf) {
|
||||
fb_attach_scene_buf_listener(fb);
|
||||
wlr_scene_node_set_position(&fb->scene_buf->node, 0, 0);
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
|
||||
}
|
||||
|
|
@ -4296,6 +4345,7 @@ wl_frame_create_child(WNativeWindow parent_win, WScreen *scr,
|
|||
? parent->tree : &wl_state.scene->tree;
|
||||
fb->scene_buf = wlr_scene_buffer_create(ptree, &fb->base);
|
||||
if (fb->scene_buf) {
|
||||
fb_attach_scene_buf_listener(fb);
|
||||
wlr_scene_node_set_position(&fb->scene_buf->node, x, y);
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
|
||||
}
|
||||
|
|
@ -7013,6 +7063,7 @@ wl_client_reparent(WNativeWindow client, WNativeWindow new_parent, int x, int y)
|
|||
: &wl_state.scene->tree);
|
||||
fb->scene_buf = wlr_scene_buffer_create(_wp, &fb->base);
|
||||
if (fb->scene_buf) {
|
||||
fb_attach_scene_buf_listener(fb);
|
||||
/* If under the frame tree, use relative coords; otherwise absolute. */
|
||||
if (parent_fb->tree)
|
||||
wlr_scene_node_set_position(&fb->scene_buf->node, x, y);
|
||||
|
|
@ -7072,7 +7123,12 @@ void wl_view_destroy_backing_impl(unsigned long id)
|
|||
struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)id);
|
||||
if (!fb) return;
|
||||
wl_list_remove(&fb->link);
|
||||
wlr_scene_node_destroy(&fb->scene_buf->node);
|
||||
/* fb->scene_buf may have been auto-destroyed by wlroots already if a
|
||||
* parent tree was destroyed earlier (e.g. wl_frame_destroy on the
|
||||
* surrounding WMaker frame). The destroy listener NULLs scene_buf
|
||||
* in that case. */
|
||||
if (fb->scene_buf)
|
||||
wlr_scene_node_destroy(&fb->scene_buf->node);
|
||||
wlr_buffer_drop(&fb->base);
|
||||
}
|
||||
|
||||
|
|
@ -7250,9 +7306,11 @@ static WNativeWindow wl_fake_leader_create(WScreen *scr)
|
|||
? wl_state.level_trees[WL_LAYER_NORMAL] : &wl_state.scene->tree;
|
||||
fb->tree = wlr_scene_tree_create(_pp);
|
||||
if (fb->tree) {
|
||||
fb_attach_tree_listener(fb);
|
||||
wlr_scene_node_set_position(&fb->tree->node, 0, 0);
|
||||
fb->scene_buf = wlr_scene_buffer_create(fb->tree, &fb->base);
|
||||
if (fb->scene_buf) {
|
||||
fb_attach_scene_buf_listener(fb);
|
||||
wlr_scene_node_set_position(&fb->scene_buf->node, 0, 0);
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
|
||||
}
|
||||
|
|
@ -7480,9 +7538,12 @@ wl_screen_create_internals(WScreen *scr)
|
|||
? wl_state.level_trees[WL_LAYER_OVERLAY] : &wl_state.scene->tree;
|
||||
fb->tree = wlr_scene_tree_create(parent);
|
||||
if (fb->tree) {
|
||||
fb_attach_tree_listener(fb);
|
||||
fb->scene_buf = wlr_scene_buffer_create(fb->tree, &fb->base);
|
||||
if (fb->scene_buf)
|
||||
if (fb->scene_buf) {
|
||||
fb_attach_scene_buf_listener(fb);
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
|
||||
}
|
||||
wlr_scene_node_set_enabled(&fb->tree->node, false);
|
||||
}
|
||||
wl_list_insert(&wl_state.frame_list, &fb->link);
|
||||
|
|
@ -8583,10 +8644,13 @@ static void wl_balloon_init(WScreen *scr)
|
|||
? wl_state.level_trees[WL_LAYER_OVERLAY] : &wl_state.scene->tree;
|
||||
fb->tree = wlr_scene_tree_create(parent);
|
||||
if (fb->tree) {
|
||||
fb_attach_tree_listener(fb);
|
||||
wlr_scene_node_set_position(&fb->tree->node, 0, 0);
|
||||
fb->scene_buf = wlr_scene_buffer_create(fb->tree, &fb->base);
|
||||
if (fb->scene_buf)
|
||||
if (fb->scene_buf) {
|
||||
fb_attach_scene_buf_listener(fb);
|
||||
wlr_scene_node_set_position(&fb->scene_buf->node, 0, 0);
|
||||
}
|
||||
wlr_scene_node_set_enabled(&fb->tree->node, false);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue