From d339ab51eb1b535362d58b4a8112e5921499399e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Apr 2026 07:10:16 +0000 Subject: [PATCH] Sanitize pane titles and window and session names more consistently and strictly, prevents C0 characters and other nonvisible characters causing problems. Reported (with a different fix) by Chris Monardo in GitHub issue 4999. --- cmd-new-session.c | 4 ++-- cmd-rename-session.c | 2 +- format.c | 4 ++-- input.c | 9 +++------ names.c | 4 +++- screen.c | 17 +++++++++++++---- session.c | 18 ------------------ spawn.c | 8 ++++++-- tmux.c | 18 ++++++++++++++++++ tmux.h | 4 ++-- window.c | 11 ++++++++--- 11 files changed, 58 insertions(+), 41 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index a3e74888..a397ccf9 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -102,7 +102,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) tmp = args_get(args, 's'); if (tmp != NULL) { name = format_single(item, tmp, c, NULL, NULL, NULL); - newname = session_check_name(name); + newname = clean_name(name, "#:."); if (newname == NULL) { cmdq_error(item, "invalid session: %s", name); free(name); @@ -142,7 +142,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) else if (groupwith != NULL) prefix = xstrdup(groupwith->name); else { - prefix = session_check_name(group); + prefix = clean_name(group, "#:."); if (prefix == NULL) { cmdq_error(item, "invalid session group: %s", group); diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 694f3c97..37b26100 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -52,7 +52,7 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item) char *newname, *tmp; tmp = format_single_from_target(item, args_string(args, 0)); - newname = session_check_name(tmp); + newname = clean_name(tmp, "#:."); if (newname == NULL) { cmdq_error(item, "invalid session: %s", tmp); free(tmp); diff --git a/format.c b/format.c index 90537879..9d93db1d 100644 --- a/format.c +++ b/format.c @@ -5235,7 +5235,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, value = format_search(search, wp, new); } free(new); - } else if (modifiers & FORMAT_REPEAT) { + } else if (modifiers & FORMAT_REPEAT) { /* Repeat multiple times. */ if (format_choose(es, copy, &left, &right, 1) != 0) { format_log(es, "repeat syntax error: %s", copy); @@ -5254,7 +5254,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, } free(right); free(left); - } else if (modifiers & FORMAT_NOT) { + } else if (modifiers & FORMAT_NOT) { value = format_bool_op_1(es, copy, 1); } else if (modifiers & FORMAT_NOT_NOT) { value = format_bool_op_1(es, copy, 0); diff --git a/input.c b/input.c index 11d481fa..d9c9344a 100644 --- a/input.c +++ b/input.c @@ -2626,12 +2626,9 @@ input_exit_osc(struct input_ctx *ictx) input_osc_4(ictx, p); break; case 7: - if (utf8_isvalid(p)) { - screen_set_path(sctx->s, p); - if (wp != NULL) { - server_redraw_window_borders(wp->window); - server_status_window(wp->window); - } + if (screen_set_path(sctx->s, p) && wp != NULL) { + server_redraw_window_borders(wp->window); + server_status_window(wp->window); } break; case 8: diff --git a/names.c b/names.c index aeb67338..8a04df73 100644 --- a/names.c +++ b/names.c @@ -166,7 +166,9 @@ parse_window_name(const char *in) if (*name == '/') name = basename(name); - name = xstrdup(name); + name = clean_name(name, "#"); free(copy); + if (name == NULL) + return (xstrdup("")); return (name); } diff --git a/screen.c b/screen.c index 617ac24c..b87c0aea 100644 --- a/screen.c +++ b/screen.c @@ -232,19 +232,28 @@ screen_set_cursor_colour(struct screen *s, int colour) int screen_set_title(struct screen *s, const char *title) { - if (!utf8_isvalid(title)) + char *new_title; + + new_title = clean_name(title, "#"); + if (new_title == NULL) return (0); free(s->title); - s->title = xstrdup(title); + s->title = new_title; return (1); } /* Set screen path. */ -void +int screen_set_path(struct screen *s, const char *path) { + char *new_path; + + new_path = clean_name(path, "#"); + if (new_path == NULL) + return (0); free(s->path); - utf8_stravis(&s->path, path, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); + s->path = new_path; + return (1); } /* Push the current title onto the stack. */ diff --git a/session.c b/session.c index 06f83a46..90894bf6 100644 --- a/session.c +++ b/session.c @@ -231,24 +231,6 @@ session_destroy(struct session *s, int notify, const char *from) session_remove_ref(s, __func__); } -/* Sanitize session name. */ -char * -session_check_name(const char *name) -{ - char *copy, *cp, *new_name; - - if (*name == '\0') - return (NULL); - copy = xstrdup(name); - for (cp = copy; *cp != '\0'; cp++) { - if (*cp == ':' || *cp == '.') - *cp = '_'; - } - utf8_stravis(&new_name, copy, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); - free(copy); - return (new_name); -} - /* Lock session if it has timed out. */ static void session_lock_timer(__unused int fd, __unused short events, void *arg) diff --git a/spawn.c b/spawn.c index 3b5fae26..1d284144 100644 --- a/spawn.c +++ b/spawn.c @@ -84,6 +84,7 @@ spawn_window(struct spawn_context *sc, char **cause) struct winlink *wl; int idx = sc->idx; u_int sx, sy, xpixel, ypixel; + char *name; spawn_log(__func__, sc); @@ -182,8 +183,11 @@ spawn_window(struct spawn_context *sc, char **cause) if (~sc->flags & SPAWN_RESPAWN) { free(w->name); if (sc->name != NULL) { - w->name = format_single(item, sc->name, c, s, NULL, - NULL); + name = format_single(item, sc->name, c, s, NULL, NULL); + w->name = clean_name(name, "#"); + free(name); + if (w->name == NULL) + w->name = xstrdup(""); options_set_number(w->options, "automatic-rename", 0); } else w->name = default_window_name(w); diff --git a/tmux.c b/tmux.c index 261cfd57..d7ca7e8b 100644 --- a/tmux.c +++ b/tmux.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "tmux.h" @@ -285,6 +286,23 @@ get_timer(void) return ((ts.tv_sec * 1000ULL) + (ts.tv_nsec / 1000000ULL)); } +char * +clean_name(const char *name, const char* forbid) +{ + char *copy, *cp, *new_name; + + if (*name == '\0' || !utf8_isvalid(name)) + return (NULL); + copy = xstrdup(name); + for (cp = copy; *cp != '\0'; cp++) { + if (strchr(forbid, *cp) != NULL) + *cp = '_'; + } + utf8_stravis(&new_name, copy, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); + free(copy); + return (new_name); +} + const char * sig2name(int signo) { diff --git a/tmux.h b/tmux.h index 00a1c94a..802278d8 100644 --- a/tmux.h +++ b/tmux.h @@ -2326,6 +2326,7 @@ int checkshell(const char *); void setblocking(int, int); char *shell_argv0(const char *, int); uint64_t get_timer(void); +char *clean_name(const char *, const char *); const char *sig2name(int); const char *find_cwd(void); const char *find_home(void); @@ -3281,7 +3282,7 @@ void screen_set_default_cursor(struct screen *, struct options *); void screen_set_cursor_style(u_int, enum screen_cursor_style *, int *); void screen_set_cursor_colour(struct screen *, int); int screen_set_title(struct screen *, const char *); -void screen_set_path(struct screen *, const char *); +int screen_set_path(struct screen *, const char *); void screen_push_title(struct screen *); void screen_pop_title(struct screen *); void screen_set_progress_bar(struct screen *, enum progress_bar_state, int); @@ -3587,7 +3588,6 @@ struct session *session_create(const char *, const char *, const char *, void session_destroy(struct session *, int, const char *); void session_add_ref(struct session *, const char *); void session_remove_ref(struct session *, const char *); -char *session_check_name(const char *); void session_update_activity(struct session *, struct timeval *); struct session *session_next_session(struct session *, struct sort_criteria *); struct session *session_previous_session(struct session *, diff --git a/window.c b/window.c index c97a3875..92337077 100644 --- a/window.c +++ b/window.c @@ -408,9 +408,14 @@ window_remove_ref(struct window *w, const char *from) void window_set_name(struct window *w, const char *new_name) { - free(w->name); - utf8_stravis(&w->name, new_name, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); - notify_window("window-renamed", w); + char *name; + + name = clean_name(new_name, "#"); + if (name != NULL) { + free(w->name); + utf8_stravis(&w->name, new_name, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); + notify_window("window-renamed", w); + } } void