From 930c81b819f8499b68a2cd5ae14ef3a57c0d7d41 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 9 Sep 2026 07:53:03 +0000 Subject: [PATCH 1/3] Do not adjust prompt position on invalid Unicode, from Kaixuan Li. --- prompt.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/prompt.c b/prompt.c index cc808a54e..300b278d8 100644 --- a/prompt.c +++ b/prompt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: prompt.c,v 1.6 2026/08/17 06:45:16 nicm Exp $ */ +/* $OpenBSD: prompt.c,v 1.7 2026/09/09 07:53:03 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott @@ -1493,9 +1493,11 @@ append_key: utf8_set(&tmp, key); if (key <= 0x1f || key == 0x7f) tmp.width = 2; - } else if (KEYC_IS_UNICODE(key)) + } else if (KEYC_IS_UNICODE(key)) { utf8_to_data(key, &tmp); - else + if (tmp.size == 0) + return (PROMPT_KEY_HANDLED); + } else return (PROMPT_KEY_HANDLED); pr->buffer = xreallocarray(pr->buffer, size + 2, From a3129249d19b74439f0c25c72124f72e47ddd9eb Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 9 Sep 2026 08:30:05 +0000 Subject: [PATCH 2/3] Bound the memory used by buffered control mode command replies (to 64 MB), GitHub issue 5565 from kagari dot shusei at proton dot me. --- control.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- tmux.h | 3 ++- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/control.c b/control.c index af0d8b91a..1da0283ff 100644 --- a/control.c +++ b/control.c @@ -1,4 +1,4 @@ -/* $OpenBSD: control.c,v 1.67 2026/09/03 21:35:38 nicm Exp $ */ +/* $OpenBSD: control.c,v 1.68 2026/09/09 08:30:05 nicm Exp $ */ /* * Copyright (c) 2012 Nicholas Marriott @@ -108,6 +108,7 @@ struct control_state { u_int pending_count; TAILQ_HEAD(, control_block) all_blocks; + size_t queued_reply_bytes; struct bufferevent *read_event; struct bufferevent *write_event; @@ -132,6 +133,9 @@ struct control_state { /* Maximum age for clients that are not using pause mode. */ #define CONTROL_MAXIMUM_AGE 300000 +/* Maximum buffered command replies for a client that is not reading. */ +#define CONTROL_MAXIMUM_REPLY_BUFFER (64 * 1024 * 1024) + /* Flags to ignore client. */ #define CONTROL_IGNORE_FLAGS \ (CLIENT_CONTROL_NOOUTPUT| \ @@ -165,6 +169,15 @@ RB_GENERATE_STATIC(control_windows, control_window, entry, control_window_cmp); static void control_free_block(struct control_state *cs, struct control_block *cb) { + size_t size; + + if (cb->size == 0 && cb->line != NULL) { + size = strlen(cb->line) + 1; + if (cs->queued_reply_bytes > size) + cs->queued_reply_bytes -= size; + else + cs->queued_reply_bytes = 0; + } free(cb->line); TAILQ_REMOVE(&cs->all_blocks, cb, all_entry); free(cb); @@ -407,16 +420,52 @@ control_reset_pane(struct client *c, struct window_pane *wp) memcpy(&cp->queued, &wp->offset, sizeof cp->queued); } +/* + * Check if the replies buffered for a client, including one about to be + * added, have grown too large and kill it if so. Returns 1 if further output + * for the client should be dropped. + */ +static int +control_check_reply_buffer(struct client *c, size_t added) +{ + struct control_state *cs = c->control_state; + size_t size; + + if (c->flags & CLIENT_CONTROL_DISCARD) + return (1); + size = EVBUFFER_LENGTH(cs->write_event->output); + size += cs->queued_reply_bytes; + size += added; + if (size < CONTROL_MAXIMUM_REPLY_BUFFER) + return (0); + + log_debug("%s: %s: %zu bytes of replies buffered", __func__, c->name, + size); + if (~c->flags & CLIENT_EXIT) { + c->exit_message = xstrdup("too far behind"); + c->flags |= CLIENT_EXIT; + control_discard(c); + } + c->flags |= CLIENT_CONTROL_DISCARD; + return (1); +} + /* Write an already-formatted line, queueing it behind %output if needed. */ static void control_write_line(struct client *c, char *line) { struct control_state *cs = c->control_state; struct control_block *cb; + size_t size = strlen(line) + 1; + + if (control_check_reply_buffer(c, size)) { + free(line); + return; + } if (TAILQ_EMPTY(&cs->all_blocks)) { log_debug("%s: %s: writing line: %s", __func__, c->name, line); - bufferevent_write(cs->write_event, line, strlen(line)); + bufferevent_write(cs->write_event, line, size - 1); bufferevent_write(cs->write_event, "\n", 1); bufferevent_enable(cs->write_event, EV_WRITE); free(line); @@ -426,6 +475,7 @@ control_write_line(struct client *c, char *line) cb = xcalloc(1, sizeof *cb); cb->line = line; TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry); + cs->queued_reply_bytes += size; cb->t = get_timer(); log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line); @@ -1007,6 +1057,7 @@ control_discard_all(struct client *c) control_discard(c); TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) control_free_block(cs, cb); + cs->queued_reply_bytes = 0; bufferevent_disable(cs->write_event, EV_WRITE); } diff --git a/tmux.h b/tmux.h index ff2158e02..57b8b3939 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1437 2026/09/09 07:03:39 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1438 2026/09/09 08:30:05 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -2261,6 +2261,7 @@ struct client { #define CLIENT_ASSUMEPASTING 0x2000000000ULL #define CLIENT_WRITE_ACK 0x4000000000ULL #define CLIENT_NO_DETACH_ON_DESTROY 0x8000000000ULL +#define CLIENT_CONTROL_DISCARD 0x1000000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ From 0544f0b21066f02659bc7b3eafdb844c95f86a36 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 9 Sep 2026 08:31:42 +0000 Subject: [PATCH 3/3] Draw bottom border with pane status line at the top. --- screen-redraw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index c47bf09d5..706540309 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1,4 +1,4 @@ -/* $OpenBSD: screen-redraw.c,v 1.158 2026/09/01 19:50:58 nicm Exp $ */ +/* $OpenBSD: screen-redraw.c,v 1.159 2026/09/09 08:31:42 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott @@ -713,7 +713,7 @@ redraw_mark_pane_borders(struct redraw_build_ctx *bctx, struct window_pane *wp, } else { mark_right = (right <= (int)bctx->w->sx); mark_bottom = (bottom <= (int)bctx->w->sy); - if (pane_status == PANE_STATUS_TOP) + if (pane_status == PANE_STATUS_TOP && bottom < (int)bctx->w->sy) mark_bottom = 0; else if (pane_status == PANE_STATUS_BOTTOM) mark_top = 0;