From: Sébastien Hoffmann Date: Sun, 14 Jun 2026 14:50:14 +0000 (+0000) Subject: patch 9.2.0642: statusline: buffer overflow with item groups X-Git-Tag: v9.2.0642^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d249884340ca7d0e5edb02445101e88a280dac68;p=thirdparty%2Fvim.git patch 9.2.0642: statusline: buffer overflow with item groups 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 "∙∙∙∙", 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: #20522 Signed-off-by: Sébastien Hoffmann Signed-off-by: Christian Brabandt --- diff --git a/src/buffer.c b/src/buffer.c index d287183293..48a6bdc3db 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -4864,7 +4864,9 @@ build_stl_str_hl_local( p = p - n + 1; // Fill up space left over by half a double-wide char. - while (++l < stl_items[stl_groupitem[groupdepth]].stl_minwid) + int minwid_fixed = MIN(stl_items[stl_groupitem[groupdepth]].stl_minwid, + stl_items[stl_groupitem[groupdepth]].stl_maxwid); + while (++l < minwid_fixed) MB_CHAR2BYTES(fillchar, p); // correct the start of the items for the truncation @@ -4880,25 +4882,30 @@ build_stl_str_hl_local( { // fill n = stl_items[stl_groupitem[groupdepth]].stl_minwid; + int fillchar_len = MB_CHAR2LEN(fillchar); if (n < 0) { // fill by appending characters n = 0 - n; - while (l++ < n && p + 1 < out + outlen) + while (l++ < n && p + fillchar_len < out + outlen) MB_CHAR2BYTES(fillchar, p); } else { // fill by inserting characters - l = (n - l) * MB_CHAR2LEN(fillchar); - mch_memmove(t + l, t, (size_t)(p - t)); + n = n - l; + l = n * fillchar_len; if (p + l >= out + outlen) - l = (long)((out + outlen) - p - 1); + { + n = (long)((out + outlen) - p - 1) / fillchar_len; + l = n * fillchar_len; + } + mch_memmove(t + l, t, (size_t)(p - t)); p += l; + for ( ; n > 0; n--) + MB_CHAR2BYTES(fillchar, t); for (n = stl_groupitem[groupdepth] + 1; n < curitem; n++) stl_items[n].stl_start += l; - for ( ; l > 0; l--) - MB_CHAR2BYTES(fillchar, t); } } continue; diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim index d4267ee929..c00582d5fd 100644 --- a/src/testdir/test_statusline.vim +++ b/src/testdir/test_statusline.vim @@ -120,10 +120,30 @@ func Test_statusline() call assert_match('^ Xstatusline\s*$', s:get_statusline()) set statusline=%.6(%f%) call assert_match('^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('^