1
0
Fork 0
mirror of https://github.com/yshui/picom.git synced 2025-04-14 17:53:25 -04:00

wm/tree: fix potential use-after-free in tree changes

If a toplevel is reparented, then destroy, any WM_TREE_CHANGE_CLIENT
events currently queued will reference the freed toplevel node.

Note this doesn't happen when the toplevel is destroyed directly.
Because in that case it will be turned into a zombie, and is guaranteed
to only be freed after the client change event is handled.

With this commit, when a toplevel is killed (i.e. destroyed or
reparented), all currently queued tree changes will be updated to
reference the zombie instead.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2024-06-26 21:44:29 +01:00
parent 7d216657d0
commit bb5d026a6f
No known key found for this signature in database
GPG key ID: D3A4405BE6CC17F4

View file

@ -40,16 +40,25 @@ static void wm_tree_enqueue_change(struct wm_tree *tree, struct wm_tree_change c
// `WM_TREE_CHANGE_TOPLEVEL_NEW` change in the queue.
bool found = false;
list_foreach_safe(struct wm_tree_change_list, i, &tree->changes, siblings) {
if (i->item.type == WM_TREE_CHANGE_TOPLEVEL_NEW &&
wm_treeid_eq(i->item.toplevel, change.toplevel)) {
if (!wm_treeid_eq(i->item.toplevel, change.toplevel)) {
continue;
}
if (i->item.type == WM_TREE_CHANGE_TOPLEVEL_NEW) {
list_remove(&i->siblings);
list_insert_after(&tree->free_changes, &i->siblings);
found = true;
} else if (wm_treeid_eq(i->item.toplevel, change.toplevel) && found) {
// We also need to delete all other changes related to
// this toplevel in between the new and gone changes.
} else if (found) {
// We also need to delete all other changes
// related to this toplevel in between the new and
// gone changes.
list_remove(&i->siblings);
list_insert_after(&tree->free_changes, &i->siblings);
} else if (i->item.type == WM_TREE_CHANGE_CLIENT) {
// Need to update client changes, so they points to the
// zombie instead of the old toplevel node, since the old
// toplevel node could be freed before tree changes are
// processed.
i->item.client.toplevel = change.killed;
}
}
if (found) {