From 1577c96ba77d65d0643a21fb7f56cf98c77ebc95 Mon Sep 17 00:00:00 2001 From: Vijay Misal Date: Mon, 10 Aug 2026 21:04:46 +0530 Subject: [PATCH 1/2] Fix capacity overflow panic wrapping a wide char at narrow terminal width InteractivePrinter::print_line's wrapping branch advances `cursor` by a chunk's full display width even when that chunk (a double-width CJK character or emoji) is wider than `cursor_max` (the terminal width). When a background is painted on the line (e.g. via --highlight-line), the end-of-line fill computed `" ".repeat(cursor_max - cursor)`, which underflows when cursor > cursor_max, aborting with "capacity overflow". Reproduced on current master: printf '\U0001F4E6\U0001F4E6\n' | bat --highlight-line 1 \ --terminal-width 1 --wrap character --color always \ --paging never --theme OneHalfDark # thread 'main' panicked at src/printer.rs:961:49: # attempt to subtract with overflow Fix clamps the fill width with saturating_sub, matching the pattern already used a few lines above (line 789-793) for the no-wrap branch and the earlier width-1 snip fix (#3804). When cursor exceeds cursor_max, the background fill is now empty instead of underflowing. Added a regression test (wide_char_wrap_at_terminal_width_one_with_highlight_does_not_panic) covering the exact repro. Full integration suite (230 tests) and clippy pass locally. Closes #3844 --- CHANGELOG.md | 1 + src/printer.rs | 2 +- tests/integration_tests.rs | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fee6615b86..876064e7a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ - `--strip-ansi`: also strip 8-bit C1 introducers (U+0090, U+0098, U+009B, U+009D, U+009E, U+009F) and DCS/SOS/PM/APC sequence bodies, which previously passed through. See #3729 (@curious-rabbit) - Fix `--ignored-suffix` not falling back to first-line/shebang detection when the ignored suffix is also a registered extension (e.g. `--ignored-suffix .txt` on a shebang script), see #2745 and #3816 (@adnrivera) - Fix `capacity overflow` panic when printing a snip separator at `--terminal-width=1` with multiple line ranges. Closes #3803, see #3804 (@leeewee) +- Fix `capacity overflow` panic when character-wrapping a line containing a character wider than `--terminal-width` with a painted background (e.g. `--highlight-line`). Closes #3844, see #3886 (@vjymisal0) - Pass `--no-paging` to `bat` invocations inside the bash / zsh / fish / PowerShell shell completion scripts so that shell-level pager wiring (e.g. `LESSOPEN='|-bat -f -pp %s'`) cannot inject ANSI escape sequences into the completion candidates. Closes #3760 (@mvanhorn) - Quote filenames before substituting them into `$LESSOPEN` / `$LESSCLOSE` templates, preventing shell injection when a filename contains shell metacharacters, see #3726 (@curious-rabbit) - Fix `--list-themes` unconditionally probing the terminal via OSC 10/11 even when `--theme` was set to an explicit value, see #3700 (regression introduced in bc42149a). (@optimistiCli) diff --git a/src/printer.rs b/src/printer.rs index 93a79e5f48..9fe9f4d26b 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -965,7 +965,7 @@ impl Printer for InteractivePrinter<'_> { write!( handle, "{}", - ansi_style.paint(" ".repeat(cursor_max - cursor)) + ansi_style.paint(" ".repeat(cursor_max.saturating_sub(cursor))) )?; } writeln!(handle)?; diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 3f15f28192..aae07ff4db 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -445,6 +445,24 @@ fn snip_at_terminal_width_one_does_not_panic() { .success(); } +#[test] +fn wide_char_wrap_at_terminal_width_one_with_highlight_does_not_panic() { + // Regression test: a character wider than the terminal (e.g. a + // double-width emoji) combined with character wrapping and a painted + // background (via --highlight-line) used to make the cursor advance + // past `cursor_max`, causing `cursor_max - cursor` to underflow and + // `str::repeat` to abort with "capacity overflow". + bat() + .arg("--highlight-line=1") + .arg("--terminal-width=1") + .arg("--wrap=character") + .arg("--color=always") + .arg("--paging=never") + .write_stdin("\u{1F4E6}\u{1F4E6}\n") + .assert() + .success(); +} + #[test] fn line_range_multiple_with_context() { bat() From 797cd06988c3e53cbb45a26609479b2e9ee148af Mon Sep 17 00:00:00 2001 From: vjymisal0 Date: Thu, 20 Aug 2026 21:08:29 +0530 Subject: [PATCH 2/2] fix(printer): avoid capacity overflow on unguarded subtraction --- src/printer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/printer.rs b/src/printer.rs index 9fe9f4d26b..83a06ec26e 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -826,7 +826,7 @@ impl Printer for InteractivePrinter<'_> { let text = self .preprocess(text.trim_end_matches(['\r', '\n']), &mut cursor_total); - let mut max_width = cursor_max - cursor; + let mut max_width = cursor_max.saturating_sub(cursor); // line buffer (avoid calling write! for every character) let mut line_buf = String::with_capacity(max_width * 4);