Merge pull request #11756 from bfredl/crashfix

custom statusline crash containing unprintable unicode
This commit is contained in:
bfredl 2023-02-14 18:51:56 +01:00 committed by GitHub
commit 9a9a4d38a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 60 additions and 30 deletions

View File

@ -3311,7 +3311,7 @@ void maketitle(void)
buf_p += MIN(size, SPACE_FOR_FNAME); buf_p += MIN(size, SPACE_FOR_FNAME);
} else { } else {
buf_p += transstr_buf((const char *)path_tail(curbuf->b_fname), buf_p += transstr_buf((const char *)path_tail(curbuf->b_fname),
buf_p, SPACE_FOR_FNAME + 1, true); -1, buf_p, SPACE_FOR_FNAME + 1, true);
} }
switch (bufIsChanged(curbuf) switch (bufIsChanged(curbuf)

View File

@ -354,14 +354,15 @@ size_t transstr_len(const char *const s, bool untab)
/// @param[in] untab remove tab characters /// @param[in] untab remove tab characters
/// ///
/// @return length of the resulting string, without the NUL byte. /// @return length of the resulting string, without the NUL byte.
size_t transstr_buf(const char *const s, char *const buf, const size_t len, bool untab) size_t transstr_buf(const char *const s, const ssize_t slen, char *const buf, const size_t buflen,
bool untab)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
const char *p = s; const char *p = s;
char *buf_p = buf; char *buf_p = buf;
char *const buf_e = buf_p + len - 1; char *const buf_e = buf_p + buflen - 1;
while (*p != NUL && buf_p < buf_e) { while ((slen < 0 || (p - s) < slen) && *p != NUL && buf_p < buf_e) {
const size_t l = (size_t)utfc_ptr2len(p); const size_t l = (size_t)utfc_ptr2len(p);
if (l > 1) { if (l > 1) {
if (buf_p + l > buf_e) { if (buf_p + l > buf_e) {
@ -416,7 +417,7 @@ char *transstr(const char *const s, bool untab)
// multi-byte characters. // multi-byte characters.
const size_t len = transstr_len(s, untab) + 1; const size_t len = transstr_len(s, untab) + 1;
char *const buf = xmalloc(len); char *const buf = xmalloc(len);
transstr_buf(s, buf, len, untab); transstr_buf(s, -1, buf, len, untab);
return buf; return buf;
} }
@ -431,7 +432,7 @@ size_t kv_transstr(StringBuilder *str, const char *const s, bool untab)
// multi-byte characters. // multi-byte characters.
const size_t len = transstr_len(s, untab); const size_t len = transstr_len(s, untab);
kv_ensure_space(*str, len + 1); kv_ensure_space(*str, len + 1);
transstr_buf(s, str->items + str->size, len + 1, untab); transstr_buf(s, -1, str->items + str->size, len + 1, untab);
str->size += len; // do not include NUL byte str->size += len; // do not include NUL byte
return len; return len;
} }

View File

@ -158,9 +158,9 @@ void grid_getbytes(ScreenGrid *grid, int row, int col, char *bytes, int *attrp)
/// attributes 'attr', and update chars[] and attrs[]. /// attributes 'attr', and update chars[] and attrs[].
/// Note: only outputs within one row, message is truncated at grid boundary! /// Note: only outputs within one row, message is truncated at grid boundary!
/// Note: if grid, row and/or col is invalid, nothing is done. /// Note: if grid, row and/or col is invalid, nothing is done.
void grid_puts(ScreenGrid *grid, char *text, int row, int col, int attr) int grid_puts(ScreenGrid *grid, char *text, int row, int col, int attr)
{ {
grid_puts_len(grid, text, -1, row, col, attr); return grid_puts_len(grid, text, -1, row, col, attr);
} }
static ScreenGrid *put_dirty_grid = NULL; static ScreenGrid *put_dirty_grid = NULL;
@ -197,7 +197,7 @@ void grid_put_schar(ScreenGrid *grid, int row, int col, char *schar, int attr)
/// like grid_puts(), but output "text[len]". When "len" is -1 output up to /// like grid_puts(), but output "text[len]". When "len" is -1 output up to
/// a NUL. /// a NUL.
void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col, int attr) int grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col, int attr)
{ {
size_t off; size_t off;
char *ptr = text; char *ptr = text;
@ -218,7 +218,7 @@ void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col,
if (grid->chars == NULL if (grid->chars == NULL
|| row >= grid->rows || row < 0 || row >= grid->rows || row < 0
|| col >= grid->cols || col < 0) { || col >= grid->cols || col < 0) {
return; return 0;
} }
if (put_dirty_row == -1) { if (put_dirty_row == -1) {
@ -230,6 +230,7 @@ void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col,
} }
} }
off = grid->line_offset[row] + (size_t)col; off = grid->line_offset[row] + (size_t)col;
int start_col = col;
// When drawing over the right half of a double-wide char clear out the // When drawing over the right half of a double-wide char clear out the
// left half. Only needed in a terminal. // left half. Only needed in a terminal.
@ -252,6 +253,12 @@ void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col,
? utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr)) ? utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr))
: utfc_ptr2char(ptr, u8cc); : utfc_ptr2char(ptr, u8cc);
int mbyte_cells = utf_char2cells(u8c); int mbyte_cells = utf_char2cells(u8c);
if (mbyte_cells > 2) {
mbyte_cells = 1;
u8c = 0xFFFD;
u8cc[0] = 0;
}
if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) { if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) {
// Do Arabic shaping. // Do Arabic shaping.
if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) { if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) {
@ -336,6 +343,7 @@ void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col,
if (do_flush) { if (do_flush) {
grid_puts_line_flush(true); grid_puts_line_flush(true);
} }
return col - start_col;
} }
/// End a group of grid_puts_len calls and send the screen buffer to the UI /// End a group of grid_puts_len calls and send the screen buffer to the UI

View File

@ -152,8 +152,8 @@ void win_redr_status(win_T *wp)
row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
col = is_stl_global ? 0 : wp->w_wincol; col = is_stl_global ? 0 : wp->w_wincol;
grid_puts(&default_grid, p, row, col, attr); int width = grid_puts(&default_grid, p, row, col, attr);
grid_fill(&default_grid, row, row + 1, len + col, grid_fill(&default_grid, row, row + 1, width + col,
this_ru_col + col, fillchar, fillchar, attr); this_ru_col + col, fillchar, fillchar, attr);
if (get_keymap_str(wp, "<%s>", NameBuff, MAXPATHL) if (get_keymap_str(wp, "<%s>", NameBuff, MAXPATHL)
@ -266,6 +266,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
int n; int n;
int fillchar; int fillchar;
char buf[MAXPATHL]; char buf[MAXPATHL];
char transbuf[MAXPATHL];
char *stl; char *stl;
char *opt_name; char *opt_name;
int opt_scope = 0; int opt_scope = 0;
@ -370,34 +371,25 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
// Make a copy, because the statusline may include a function call that // Make a copy, because the statusline may include a function call that
// might change the option value and free the memory. // might change the option value and free the memory.
stl = xstrdup(stl); stl = xstrdup(stl);
int width = build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name, opt_scope, build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name, opt_scope,
fillchar, maxwidth, &hltab, &tabtab, NULL); fillchar, maxwidth, &hltab, &tabtab, NULL);
xfree(stl); xfree(stl);
ewp->w_p_crb = p_crb_save; ewp->w_p_crb = p_crb_save;
// Make all characters printable. int len = (int)strlen(buf);
char *p = transstr(buf, true); int start_col = col;
int len = (int)xstrlcpy(buf, p, sizeof(buf));
len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1;
xfree(p);
// fill up with "fillchar"
while (width < maxwidth && len < (int)sizeof(buf) - 1) {
len += utf_char2bytes(fillchar, buf + len);
width++;
}
buf[len] = NUL;
// Draw each snippet with the specified highlighting. // Draw each snippet with the specified highlighting.
grid_puts_line_start(grid, row); grid_puts_line_start(grid, row);
int curattr = attr; int curattr = attr;
p = buf; char *p = buf;
for (n = 0; hltab[n].start != NULL; n++) { for (n = 0; hltab[n].start != NULL; n++) {
int textlen = (int)(hltab[n].start - p); int textlen = (int)(hltab[n].start - p);
grid_puts_len(grid, p, textlen, row, col, curattr); // Make all characters printable.
col += vim_strnsize(p, textlen); size_t tsize = transstr_buf(p, textlen, transbuf, sizeof transbuf, true);
col += grid_puts_len(grid, transbuf, (int)tsize, row, col, curattr);
p = hltab[n].start; p = hltab[n].start;
if (hltab[n].userhl == 0) { if (hltab[n].userhl == 0) {
@ -411,7 +403,12 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
} }
} }
// Make sure to use an empty string instead of p, if p is beyond buf + len. // Make sure to use an empty string instead of p, if p is beyond buf + len.
grid_puts(grid, p >= buf + len ? "" : p, row, col, curattr); size_t tsize = transstr_buf(p >= buf + len ? "" : p, -1, transbuf, sizeof transbuf, true);
col += grid_puts_len(grid, transbuf, (int)tsize, row, col, curattr);
int maxcol = start_col + maxwidth;
// fill up with "fillchar"
grid_fill(grid, row, row + 1, col, maxcol, fillchar, fillchar, curattr);
grid_puts_line_flush(false); grid_puts_line_flush(false);

View File

@ -158,6 +158,7 @@ describe('multibyte rendering: statusline', function()
screen:set_default_attr_ids({ screen:set_default_attr_ids({
[1] = {bold = true, foreground = Screen.colors.Blue1}, [1] = {bold = true, foreground = Screen.colors.Blue1},
[2] = {bold = true, reverse = true}, [2] = {bold = true, reverse = true},
[3] = {background = Screen.colors.Red, foreground = Screen.colors.Gray100};
}) })
screen:attach() screen:attach()
command('set laststatus=2') command('set laststatus=2')
@ -220,4 +221,27 @@ describe('multibyte rendering: statusline', function()
| |
]]} ]]}
end) end)
it('unprintable chars in filename with default stl', function()
command("file 🧑‍💻")
-- TODO: this is wrong but avoids a crash
screen:expect{grid=[[
^ |
{1:~ }|
{2:🧑<EFBFBD>💻 }|
|
]]}
end)
it('unprintable chars in filename with custom stl', function()
command('set statusline=xx%#ErrorMsg#%f%##yy')
command("file 🧑‍💻")
-- TODO: this is also wrong but also avoids a crash
screen:expect{grid=[[
^ |
{1:~ }|
{2:xx}{3:🧑<200d>💻}{2:yy }|
|
]]}
end)
end) end)