vim-patch:9.2.0642: statusline: buffer overflow with item groups (#40253)

Problem:  statusline: buffer overflow with item groups
Solution: Fix the issues (see below) (Sébastien Hoffmann)

Fix various buffer overflow bugs (examples assume MAXPATHL==4096):
- truncated item groups where minwid>maxwid:
    vim --clean +"set ls=2 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:
    vim --clean +"set ls=2 fillchars+=stl:∙ stl=%<%{%repeat('x',4096-3)%}%-2(X%)"
  wrongly leads to padding at the end of the statusline and `p-out==4097`
  because the bounds check assumes a 1-byte fillchar
- right-aligned item groups with 1-byte fillchar:
    vim --clean +"set ls=2 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:
    vim --clean +"set ls=2 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 vim,
    vim --clean +"set ls=2 fillchars+=stl:∙ stl=%<%{%repeat('x',4096-149)%}%50(X%)"

related: neovim/neovim#40219
closes:  vim/vim#20522

d249884340

Co-authored-by: Sébastien Hoffmann <contact@shoffmann.dev>
This commit is contained in:
zeertzjq
2026-06-15 10:40:25 +08:00
committed by GitHub
parent ff6930a646
commit 63d89afc4c
2 changed files with 27 additions and 7 deletions

View File

@@ -1147,8 +1147,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.
minwid = MIN(minwid, maxwid);
while (++group_len < minwid) {
int minwid_fixed = MIN(minwid, maxwid);
while (++group_len < minwid_fixed) {
schar_get_adv(&out_p, fillchar);
}
// }
@@ -1187,15 +1187,15 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
out_p += added_bytes;
// }
// Adjust item start positions
for (int n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
stl_items[n].start += added_bytes;
}
// Prepend the fill characters
for (; added_cells > 0; added_cells--) {
schar_get_adv(&t, fillchar);
}
// Adjust item start positions
for (int n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
stl_items[n].start += added_bytes;
}
}
}
continue;

View File

@@ -124,10 +124,30 @@ func Test_statusline()
call assert_match('^ Xstatusline\s*$', s:get_statusline())
set statusline=%.6(%f%)
call assert_match('^<sline\s*$', s:get_statusline())
set statusline=%.5(1234567%),%2.5(1234567%),%5.5(1234567%),%50.5(1234567%)
call assert_match('^<4567,<4567,<4567,<4567\s*$', s:get_statusline())
set statusline=%14f
call assert_match('^ Xstatusline\s*$', s:get_statusline())
set statusline=%.4L
call assert_match('^10>3\s*$', s:get_statusline())
for filler in ['-', '∙']
exec 'set fillchars+=stl:'..filler
set statusline=%-5(x%),%-5.(x%),%-5.5(x%),%-5.10(x%);
call assert_match(substitute('^x____,x____,x____,x____;_*$', '_', filler, 'g'), s:get_statusline())
set statusline=%5(x%),%5.(x%),%5.5(x%),%5.10(x%);
call assert_match(substitute('^____x,____x,____x,____x;_*$', '_', filler, 'g'), s:get_statusline())
set statusline=%.5(12🙂345%),%4.5(12🙂345%),%5.5(12🙂345%),%50.5(12🙂345%);
call assert_match(substitute('^<345,<345,<345_,<345_;_*$', '_', filler, 'g'), s:get_statusline())
endfor
if has('linux')
" This assumes MAXPATHL is 4096 bytes.
set stl=%{%repeat('x',4096-6)%}%10(X%)
set fillchars+=stl:-
call assert_match('^<x\+----X$', s:get_statusline())
set fillchars+=stl:∙
call assert_match('^<x\+∙X$', s:get_statusline())
endif
set fillchars&
" %h: Help buffer flag, text is "[help]".
" %H: Help buffer flag, text is ",HLP".