mirror of
https://github.com/neovim/neovim.git
synced 2026-08-01 05:09:08 +00:00
fix(statusline): prevent various buffer overflows with item groups #40219
Problem:
Various out-of-bounds writes inherited from vim (examples assume MAXPATHL==4096):
- truncated item groups where minwid>maxwid:
nvim --clean +"set stl=%<%{%repeat('x',4096-11)%}%50.5(12🙂345%)"
leads to fillchars spilling over the end of the group/buffer while trying to
compensate for truncating at a multicell character because minwid<=maxwid is assumed
- left-aligned item groups with multi-byte fillchar:
nvim --clean +"set fillchars+=stl:∙ stl=%<%{%repeat('x',4096-3)%}%-2(X%)"
wrongly leads to padding at the end of the statusline and `out_p-out==4097`
because the bounds check assumes a 1-byte fillchar
- right-aligned item groups with 1-byte fillchar:
nvim --clean +"set stl=%<%{%repeat('x',4096-4)%}%4(XY%)"
leads to "YX" instead of "XY" at the end of the statusline
because `memmove` is done before adjusting the offset
- right-aligned item groups with multi-byte fillchar:
nvim --clean +"set fillchars+=stl:∙ stl=%5(X%)"
leads to "∙∙∙∙<e2>", i.e. the fillchar is being written over the group contents
and eventually being overwritten itself at the second byte with the final NUL,
because the padding counter assumes a 1-byte fillchar; to crash Neovim,
nvim --clean +"set fillchars+=stl:∙ stl=%<%{%repeat('x',4096-149)%}%50(X%)"
Solution:
Clearer variable names and no recycling of variables for different purposes.
(cherry picked from commit e36659c82f)
This commit is contained in:
committed by
github-actions[bot]
parent
c1318ac348
commit
83d9b9afca
@@ -1119,16 +1119,19 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
|
||||
}
|
||||
}
|
||||
|
||||
int minwid = stl_items[stl_groupitems[groupdepth]].minwid;
|
||||
|
||||
// If the group is longer than it is allowed to be truncate by removing
|
||||
// bytes from the start of the group text. Don't truncate when item is a
|
||||
// 'statuscolumn' fold item to ensure correctness of the mouse clicks.
|
||||
if (group_len > stl_items[stl_groupitems[groupdepth]].maxwid
|
||||
&& stl_items[stl_groupitems[groupdepth]].type != HighlightFold) {
|
||||
// { Determine the number of bytes to remove
|
||||
int maxwid = stl_items[stl_groupitems[groupdepth]].maxwid;
|
||||
|
||||
// Find the first character that should be included.
|
||||
int n = 0;
|
||||
while (group_len >= stl_items[stl_groupitems[groupdepth]].maxwid) {
|
||||
while (group_len >= maxwid) {
|
||||
group_len -= ptr2cells(t + n);
|
||||
n += utfc_ptr2len(t + n);
|
||||
}
|
||||
@@ -1141,7 +1144,8 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
|
||||
memmove(t + 1, t + n, (size_t)(out_p - (t + n)));
|
||||
out_p = out_p - n + 1;
|
||||
// Fill up space left over by half a double-wide char.
|
||||
while (++group_len < stl_items[stl_groupitems[groupdepth]].minwid) {
|
||||
minwid = MIN(minwid, maxwid);
|
||||
while (++group_len < minwid) {
|
||||
schar_get_adv(&out_p, fillchar);
|
||||
}
|
||||
// }
|
||||
@@ -1157,33 +1161,36 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
|
||||
stl_items[idx].start = MAX(stl_items[idx].start, t);
|
||||
}
|
||||
// If the group is shorter than the minimum width, add padding characters.
|
||||
} else if (abs(stl_items[stl_groupitems[groupdepth]].minwid) > group_len) {
|
||||
ptrdiff_t min_group_width = stl_items[stl_groupitems[groupdepth]].minwid;
|
||||
} else if (abs(minwid) > group_len) {
|
||||
ptrdiff_t fillchar_bytes = (ptrdiff_t)schar_len(fillchar);
|
||||
// If the group is left-aligned, add characters to the right.
|
||||
if (min_group_width < 0) {
|
||||
min_group_width = 0 - min_group_width;
|
||||
while (group_len++ < min_group_width && out_p < out_end_p) {
|
||||
if (minwid < 0) {
|
||||
minwid = 0 - minwid;
|
||||
while (group_len++ < minwid && out_p + fillchar_bytes <= out_end_p) {
|
||||
schar_get_adv(&out_p, fillchar);
|
||||
}
|
||||
// If the group is right-aligned, shift everything to the right and
|
||||
// prepend with filler characters.
|
||||
} else {
|
||||
// { Move the group to the right
|
||||
group_len = (min_group_width - group_len) * (int)schar_len(fillchar);
|
||||
memmove(t + group_len, t, (size_t)(out_p - t));
|
||||
if (out_p + group_len >= (out_end_p + 1)) {
|
||||
group_len = out_end_p - out_p;
|
||||
ptrdiff_t added_cells = minwid - group_len;
|
||||
ptrdiff_t added_bytes = added_cells * fillchar_bytes;
|
||||
if (out_p + added_bytes > out_end_p) {
|
||||
added_cells = (out_end_p - out_p) / fillchar_bytes;
|
||||
added_bytes = added_cells * fillchar_bytes;
|
||||
}
|
||||
out_p += group_len;
|
||||
|
||||
// { Move the group to the right
|
||||
memmove(t + added_bytes, t, (size_t)(out_p - t));
|
||||
out_p += added_bytes;
|
||||
// }
|
||||
|
||||
// Adjust item start positions
|
||||
for (int n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
|
||||
stl_items[n].start += group_len;
|
||||
stl_items[n].start += added_bytes;
|
||||
}
|
||||
|
||||
// Prepend the fill characters
|
||||
for (; group_len > 0; group_len--) {
|
||||
for (; added_cells > 0; added_cells--) {
|
||||
schar_get_adv(&t, fillchar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,6 +429,40 @@ describe('build_stl_str_hl', function()
|
||||
'aaaa~~b~~c~~d~~e~~fg~~hi~~jk~~lmnop~~qrstuv~~~wxyz'
|
||||
)
|
||||
|
||||
-- item group testing
|
||||
|
||||
statusline_test_align(
|
||||
'should right align in right aligned groups',
|
||||
30,
|
||||
'%5(%l%), %5.(%l%), %5.5(%l%), %5.10(%l%)',
|
||||
'~~~~0, ~~~~0, ~~~~0, ~~~~0',
|
||||
{ expected_cell_count = 26 }
|
||||
)
|
||||
|
||||
statusline_test_align(
|
||||
'should left align in left aligned groups',
|
||||
30,
|
||||
'%-5(%l%), %-5.(%l%), %-5.5(%l%), %-5.10(%l%)',
|
||||
'0~~~~, 0~~~~, 0~~~~, 0~~~~',
|
||||
{ expected_cell_count = 26 }
|
||||
)
|
||||
|
||||
statusline_test(
|
||||
'Should truncate groups according to maxwid',
|
||||
30,
|
||||
'%.5(1234567%), %2.5(1234567%), %5.5(1234567%)',
|
||||
'<4567, <4567, <4567',
|
||||
{ expected_cell_count = 19 }
|
||||
)
|
||||
|
||||
statusline_test_align(
|
||||
'Should compensate with fillchar after truncating item group at multicell character',
|
||||
20,
|
||||
'%.5(12🙂345%), %5.5(12🙂345%), %50.5(12🙂345%)',
|
||||
'<345, <345~, <345~',
|
||||
{ expected_cell_count = 18 }
|
||||
)
|
||||
|
||||
-- stl item testing
|
||||
local tabline = ''
|
||||
for i = 1, 1000 do
|
||||
|
||||
Reference in New Issue
Block a user