fix use-after-free: detach reparented children before tree destroy
When destroying a frame_buf tree, any other frame_bufs whose trees were reparented into it (e.g. fake_leaders) would have their tree_destroy_handler fire on already-freed memory during cascade. Detach their listeners and NULL their tree pointers before destroying the parent tree.
This commit is contained in:
parent
01904fdb39
commit
a6f43cdbe1
|
|
@ -28,6 +28,12 @@ extern void *W_GetViewBacking(unsigned long id);
|
||||||
void frame_buf_destroy(struct wlr_buffer *wlr_buf)
|
void frame_buf_destroy(struct wlr_buffer *wlr_buf)
|
||||||
{
|
{
|
||||||
struct wl_frame_buf *fb = wl_container_of(wlr_buf, fb, base);
|
struct wl_frame_buf *fb = wl_container_of(wlr_buf, fb, base);
|
||||||
|
/* Unlink destroy listeners before freeing so cascade signals
|
||||||
|
* from parent tree destruction can't reach this freed struct. */
|
||||||
|
if (fb->tree_destroy.link.next)
|
||||||
|
wl_list_remove(&fb->tree_destroy.link);
|
||||||
|
if (fb->scene_buf_destroy.link.next)
|
||||||
|
wl_list_remove(&fb->scene_buf_destroy.link);
|
||||||
if (fb->bg_pixmap)
|
if (fb->bg_pixmap)
|
||||||
pixman_image_unref(fb->bg_pixmap);
|
pixman_image_unref(fb->bg_pixmap);
|
||||||
if (fb->image)
|
if (fb->image)
|
||||||
|
|
@ -472,11 +478,26 @@ wl_frame_destroy(WScreen *scr, WNativeWindow win)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
W_UnregisterBacking(fb->id);
|
/* Before destroying the tree, detach any other frame_bufs whose
|
||||||
/* Destroy the scene_buf and (if any) the tree. Without destroying
|
* trees are descendants of ours (e.g. reparented fake_leaders) so
|
||||||
* the tree, leaked tree nodes accumulate in the scene graph and
|
* their tree_destroy_handler doesn't fire on freed memory. */
|
||||||
* can cause stale rendering or hit-testing issues (e.g. ghost
|
if (fb->tree) {
|
||||||
* titlebars after a menu is closed). */
|
struct wl_frame_buf *other, *otmp;
|
||||||
|
wl_list_for_each_safe(other, otmp, &wl_state.frame_list, link) {
|
||||||
|
if (other == fb || !other->tree) continue;
|
||||||
|
struct wlr_scene_tree *p = other->tree->node.parent;
|
||||||
|
while (p) {
|
||||||
|
if (p == fb->tree) {
|
||||||
|
wl_list_remove(&other->tree_destroy.link);
|
||||||
|
wl_list_init(&other->tree_destroy.link);
|
||||||
|
other->tree = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p = p->node.parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (fb->scene_buf)
|
if (fb->scene_buf)
|
||||||
wlr_scene_node_destroy(&fb->scene_buf->node);
|
wlr_scene_node_destroy(&fb->scene_buf->node);
|
||||||
if (fb->tree)
|
if (fb->tree)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue