From d22ab85b84fbeaacc933e393e7225f4e7927000a Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Mar 2026 08:45:30 +0000 Subject: [PATCH 1/5] Protect against overflow when scrollbar is off screen, from san65384 at gmail dot com in GitHub issue 4933. --- screen-redraw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/screen-redraw.c b/screen-redraw.c index 138edd52..88653f34 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1091,12 +1091,13 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx, sy += ctx->statuslines; } - /* Set up style for slider. */ gc = sb_style->gc; memcpy(&slgc, &gc, sizeof slgc); slgc.fg = gc.bg; slgc.bg = gc.fg; + if (sb_x >= sx || sb_y >= sy) + return; imax = sb_w + sb_pad; if ((int)imax + sb_x > sx) imax = sx - sb_x; From d70edfa0a051d0f845890e0e58a50699f336e153 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Mar 2026 08:48:32 +0000 Subject: [PATCH 2/5] Fix issue where popup window gets overwritten by background updates, from Conor Taylor in GitHub issue 4920. --- tty-draw.c | 31 ++++++++++++++++++++++--------- tty.c | 39 ++++++++++++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/tty-draw.c b/tty-draw.c index fa9cbcd5..bd643f98 100644 --- a/tty-draw.c +++ b/tty-draw.c @@ -48,12 +48,19 @@ static void tty_draw_line_clear(struct tty *tty, u_int px, u_int py, u_int nx, const struct grid_cell *defaults, u_int bg, int wrapped) { + struct visible_ranges *r; + struct visible_range *rr; + u_int i; + /* Nothing to clear. */ if (nx == 0) return; /* If genuine BCE is available, can try escape sequences. */ - if (!wrapped && nx >= 10 && !tty_fake_bce(tty, defaults, bg)) { + if (tty->client->overlay_check == NULL && + !wrapped && + nx >= 10 && + !tty_fake_bce(tty, defaults, bg)) { /* Off the end of the line, use EL if available. */ if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) { tty_cursor(tty, px, py); @@ -77,14 +84,20 @@ tty_draw_line_clear(struct tty *tty, u_int px, u_int py, u_int nx, } /* Couldn't use an escape sequence, use spaces. */ - if (px != 0 || !wrapped) - tty_cursor(tty, px, py); - if (nx == 1) - tty_putc(tty, ' '); - else if (nx == 2) - tty_putn(tty, " ", 2, 2); - else - tty_repeat_space(tty, nx); + r = tty_check_overlay_range(tty, px, py, nx); + for (i = 0; i < r->used; i++) { + rr = &r->ranges[i]; + if (rr->nx != 0) { + if (rr->px != 0 || !wrapped) + tty_cursor(tty, rr->px, py); + if (rr->nx == 1) + tty_putc(tty, ' '); + else if (rr->nx == 2) + tty_putn(tty, " ", 2, 2); + else + tty_repeat_space(tty, rr->nx); + } + } } /* Is this cell empty? */ diff --git a/tty.c b/tty.c index d05d2dc2..41cb71c1 100644 --- a/tty.c +++ b/tty.c @@ -1157,6 +1157,9 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py, u_int px, u_int nx, u_int bg) { struct client *c = tty->client; + struct visible_ranges *r; + struct visible_range *rr; + u_int i; log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); @@ -1192,8 +1195,14 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py, * Couldn't use an escape sequence, use spaces. Clear only the visible * bit if there is an overlay. */ - tty_cursor(tty, px, py); - tty_repeat_space(tty, nx); + r = tty_check_overlay_range(tty, px, py, nx); + for (i = 0; i < r->used; i++) { + rr = &r->ranges[i]; + if (rr->nx != 0) { + tty_cursor(tty, rr->px, py); + tty_repeat_space(tty, rr->nx); + } + } } /* Clear a line, adjusting to visible part of pane. */ @@ -1359,18 +1368,34 @@ static void tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py) { struct screen *s = ctx->s; - u_int nx = ctx->sx, i, x, rx, ry; + u_int nx = ctx->sx, i, x, rx, ry, j; + struct visible_ranges *r; + struct visible_range *rr; log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger); if (!ctx->bigger) { - tty_draw_line(tty, s, 0, py, nx, ctx->xoff, ctx->yoff + py, - &ctx->defaults, ctx->palette); + r = tty_check_overlay_range(tty, ctx->xoff, ctx->yoff + py, nx); + for (j = 0; j < r->used; j++) { + rr = &r->ranges[j]; + if (rr->nx != 0) { + tty_draw_line(tty, s, rr->px - ctx->xoff, py, + rr->nx, rr->px, ctx->yoff + py, + &ctx->defaults, ctx->palette); + } + } return; } if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) { - tty_draw_line(tty, s, i, py, rx, x, ry, &ctx->defaults, - ctx->palette); + r = tty_check_overlay_range(tty, x, ry, rx); + for (j = 0; j < r->used; j++) { + rr = &r->ranges[j]; + if (rr->nx != 0) { + tty_draw_line(tty, s, i + (rr->px - x), py, + rr->nx, rr->px, ry, &ctx->defaults, + ctx->palette); + } + } } } From 3909d7e92f71ffe5e86e081ed831f1808701d82d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Mar 2026 08:58:39 +0000 Subject: [PATCH 3/5] Use window options for cursor-style to avoid crash when no pane, from Arden Packeer in GitHub issue 4942. --- input.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/input.c b/input.c index 6c1a66e4..24467144 100644 --- a/input.c +++ b/input.c @@ -1613,7 +1613,7 @@ input_csi_dispatch(struct input_ctx *ictx) if (ictx->wp != NULL) oo = ictx->wp->options; else - oo = global_options; + oo = global_w_options; p = options_get_number(oo, "cursor-style"); /* blink for 1,3,5; steady for 0,2,4,6 */ @@ -2503,7 +2503,7 @@ input_handle_decrqss(struct input_ctx *ictx) if (wp != NULL) oo = wp->options; else - oo = global_options; + oo = global_w_options; opt_ps = options_get_number(oo, "cursor-style"); /* Sanity clamp: valid Ps are 0..6 per DECSCUSR. */ @@ -2538,8 +2538,9 @@ input_dcs_dispatch(struct input_ctx *ictx) long long allow_passthrough = 0; if (wp == NULL) - return (0); - oo = wp->options; + oo = global_w_options; + else + oo = wp->options; if (ictx->flags & INPUT_DISCARD) { log_debug("%s: %zu bytes (discard)", __func__, len); From b88c08f8603036999dd7f9900ab51f6a646d0fab Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Mar 2026 09:03:43 +0000 Subject: [PATCH 4/5] Fix a NULL dereference and use after free, GitHub issue 4936 from Pavel Lavrukhin. --- mode-tree.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mode-tree.c b/mode-tree.c index 0cd17e39..835f4219 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1173,7 +1173,7 @@ mode_tree_display_help(__unused struct mode_tree_data *mtd, struct client *c) } for (line = mode_tree_help_start; *line != NULL; line++) h++; - for (line = lines; *line != NULL; line++) + for (line = lines; line != NULL && *line != NULL; line++) h++; for (line = mode_tree_help_end; *line != NULL; line++) h++; @@ -1194,7 +1194,7 @@ mode_tree_display_help(__unused struct mode_tree_data *mtd, struct client *c) popup_write(c, new_line, strlen(new_line)); free(new_line); } - for (line = lines; *line != NULL; line++) { + for (line = lines; line != NULL && *line != NULL; line++) { new_line = cmd_template_replace(*line, item, 1); popup_write(c, new_line, strlen(new_line)); free(new_line); @@ -1214,7 +1214,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, struct mode_tree_line *line; struct mode_tree_item *current, *parent, *mti; u_int i, x, y; - int choice; + int choice, preview; if (KEYC_IS_MOUSE(*key) && m != NULL) { if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) { @@ -1226,9 +1226,10 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, if (yp != NULL) *yp = y; if (x > mtd->width || y > mtd->height) { + preview = mtd->preview; if (*key == KEYC_MOUSEDOWN3_PANE) mode_tree_display_menu(mtd, c, x, y, 1); - if (mtd->preview == MODE_TREE_PREVIEW_OFF) + if (preview == MODE_TREE_PREVIEW_OFF) *key = KEYC_NONE; return (0); } From f7dad4f38f6cc9ef620a05c8f79b297547f1f533 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Mar 2026 09:05:59 +0000 Subject: [PATCH 5/5] Check lastgc is not NULL before using it, GitHub issue 4935 from Pavel Lavrukhin. --- grid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid.c b/grid.c index d4955f96..d5d2b37f 100644 --- a/grid.c +++ b/grid.c @@ -1117,7 +1117,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, if (gc.flags & GRID_FLAG_PADDING) continue; - if (flags & GRID_STRING_WITH_SEQUENCES) { + if (lastgc != NULL && (flags & GRID_STRING_WITH_SEQUENCES)) { grid_string_cells_code(*lastgc, &gc, code, sizeof code, flags, s, &has_link); codelen = strlen(code);