From cf69385823d30fbc6d9995dea4c2eb3cc31892bf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 12 May 2025 13:53:12 +0530 Subject: [PATCH] Wayland: Fix an abort if the terminal program sets a window title longer than 2KB that contains CSI escape sequences and multibyte UTF-8 Fixes #8619 --- docs/changelog.rst | 2 ++ kitty/glfw.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index caebb6680..41023721d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -113,6 +113,8 @@ Detailed list of changes - macOS: Quick access terminal: Restore focus to previously active window when hiding the quick access terminal window (:iss:`8627`) +- Wayland: Fix an abort if the terminal program sets a window title longer than 2KB that contains CSI escape sequences and multibyte UTF-8 (:iss:`8619`) + 0.42.0 [2025-05-11] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/kitty/glfw.c b/kitty/glfw.c index 7d5d2e455..f3d9edcd8 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -70,6 +70,52 @@ on_clipboard_lost(GLFWClipboardType which) { call_boss(on_clipboard_lost, "s", which == GLFW_CLIPBOARD ? "clipboard" : "primary"); } +static bool +is_continuation_byte(unsigned char byte) { + return (byte & 0xC0) == 0x80; // Continuation bytes have the form 10xxxxxx +} + +static int +utf8_sequence_length(unsigned char byte) { + if ((byte & 0x80) == 0) return 1; // 0xxxxxxx: Single-byte ASCII + if ((byte & 0xE0) == 0xC0) return 2; // 110xxxxx: Two-byte sequence + if ((byte & 0xF0) == 0xE0) return 3; // 1110xxxx: Three-byte sequence + if ((byte & 0xF8) == 0xF0) return 4; // 11110xxx: Four-byte sequence + return -1; // Invalid first byte +} + +// Function to remove invalid UTF-8 bytes from the end of a string +static void +remove_invalid_utf8_from_end(char *str, size_t len) { + if (!len) return; + // Start from the end of the string and move backward + size_t i = len - 1; + while (i > 0) { + if (is_continuation_byte((unsigned char)str[i])) { + // Continue backward to find the start of the potential UTF-8 sequence + size_t start = i; + while (start > 0 && is_continuation_byte((unsigned char)str[start])) start--; + // Check if the sequence is valid + int seq_len = utf8_sequence_length((unsigned char)str[start]); + if (seq_len > 0 && start + seq_len == len) return; // Valid sequence found, stop trimming + // Invalid sequence, trim it + str[start] = '\0'; + len = start; + i = start - 1; + } else { + // Not a continuation byte, check if it's a valid start byte + int seq_len = utf8_sequence_length((unsigned char)str[i]); + if (seq_len > 0 && i + seq_len == len) return; // Valid sequence found, stop trimming + // Invalid byte, trim it + str[i] = '\0'; + len = i; + i--; + } + } + // Handle the case where the entire string is invalid + if (utf8_sequence_length((unsigned char)str[0]) < 0) str[0] = '\0'; +} + static void strip_csi_(const char *title, char *buf, size_t bufsz) { enum { NORMAL, IN_ESC, IN_CSI} state = NORMAL; @@ -99,6 +145,7 @@ strip_csi_(const char *title, char *buf, size_t bufsz) { } } *dest = 0; + remove_invalid_utf8_from_end(buf, dest - buf); }