From 63d89afc4c82a53b74bf16a02e0774bc199912bd Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 15 Jun 2026 10:40:25 +0800 Subject: [PATCH] vim-patch:9.2.0642: statusline: buffer overflow with item groups (#40253) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: vim/vim#20522 https://github.com/vim/vim/commit/d249884340ca7d0e5edb02445101e88a280dac68 Co-authored-by: SΓ©bastien Hoffmann --- src/nvim/statusline.c | 14 +++++++------- test/old/testdir/test_statusline.vim | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index d409858eb0..6b7ecd67dc 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -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; diff --git a/test/old/testdir/test_statusline.vim b/test/old/testdir/test_statusline.vim index 9685d42790..6d6b8818f7 100644 --- a/test/old/testdir/test_statusline.vim +++ b/test/old/testdir/test_statusline.vim @@ -124,10 +124,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('^