window: fix use-after-free destroying a zoomed window

window_destroy() called window_unzoom() before tearing anything down.
layout_fix_panes() inside that resizes panes, which fires the
pane-resized hook; the hook's event payload takes and drops its own
reference on the window, and since w->references was already 0 at that
point, dropping it reached 0 again and re-entered window_destroy() from
inside itself - freeing w and its panes while the outer call was still
using them.

Pin the window's reference count across window_unzoom() so that
reentrant drop can't reach zero again.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Michael Grant
2026-09-20 22:37:59 +01:00
parent 610b37b75b
commit 2f1abeeb1a

View File

@@ -452,7 +452,18 @@ window_destroy(struct window *w)
{
log_debug("window @%u destroyed (%d references)", w->id, w->references);
/*
* Pin the window while unzooming: layout_fix_panes() resizes panes,
* which fires the pane-resized hook, and its event payload takes and
* drops its own reference on the window. references is already 0
* here, so that reference reaching 0 again would call window_destroy()
* a second time from inside this call, freeing w (and its panes)
* out from under the rest of this function.
*/
w->references++;
window_unzoom(w, 0);
w->references--;
RB_REMOVE(windows, &windows, w);
layout_free_cell(w->layout_root, 0);