wayland: fix titlebar height overflow in frame_paint

The titlebar child frame_buf can have a height smaller than tb_h
(fwin->top_width clamped to total_h). The existing bounds check only
verified width, not height. The blit loop wrote tb_h rows into a
shorter pixman image, overflowing into adjacent heap allocations.

This caused 100%% reproducible SIGSEGV in malloc (heap corruption
detected by glibc) on any operation following a frame paint where the
titlebar child was shorter than expected — e.g. opening the Run dialog
after a menu frame was painted.

Fix: check both width AND height before using the titlebar child buffer.
Fall back to drawing into the parent frame buffer (which is correctly
sized) when either dimension is insufficient.
This commit is contained in:
lkn 2026-06-24 22:06:30 +02:00
parent de15ca2282
commit e00bc6b25a
1 changed files with 7 additions and 5 deletions

View File

@ -588,6 +588,7 @@ wl_frame_paint(struct WFrameWindow *fwin)
int state = fwin->flags.state;
int total_w = fb->width;
int total_h = fb->height;
int tb_in_parent = 0;
/* Clear the buffer to transparent black. */
pixman_color_t clear = {0, 0, 0, 0}; /* transparent */
pixman_image_fill_rectangles(PIXMAN_OP_SRC, fb->image, &clear,
@ -614,11 +615,12 @@ wl_frame_paint(struct WFrameWindow *fwin)
wlr_scene_node_set_position(&tb_fb->scene_buf->node, 0, 0);
}
/* If titlebar buffer is too narrow, draw directly into the
* parent frame buffer to avoid unsafe in-place resize. */
if (tb_fb && tb_fb->width < tb_w) {
/* If titlebar buffer is too small, draw directly into the
* parent frame buffer to avoid buffer overflow. */
if (tb_fb && (tb_fb->width < tb_w || tb_fb->height < tb_h)) {
tb_img = fb->image;
tb_fb = NULL;
tb_in_parent = 1;
}
WTexture *tex = fwin->title_texture[state];
@ -815,9 +817,9 @@ wl_frame_paint(struct WFrameWindow *fwin)
/* Commit the parent frame buffer when it has visible content:
* resizebar pixels (unshaded) or titlebar fallback (when the titlebar
* child buffer is too narrow and we drew into the parent instead). */
* child buffer was too small and we drew into the parent instead). */
if (fb->scene_buf) {
if (show_resizebar || !fwin->titlebar || !frame_buf_find(fwin->titlebar->window)) {
if (show_resizebar || tb_in_parent) {
wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL);
wlr_scene_node_set_enabled(&fb->scene_buf->node, true);
} else {