From: Christian Brabandt Date: Thu, 9 Apr 2026 22:27:36 +0000 (+0000) Subject: patch 9.2.0331: spellfile: stack buffer overflows in spell file generation X-Git-Tag: v9.2.0331^0 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=07faa961a05bc5ea007ab70ff483ea1b32c3371d;p=thirdparty%2Fvim.git patch 9.2.0331: spellfile: stack buffer overflows in spell file generation Problem: spell_read_aff() uses sprintf() into a fixed-size stack buffer without bounds checking. store_aff_word() uses STRCAT() to append attacker-controlled strings into newword[MAXWLEN] without checking remaining space. Both are reachable via :mkspell with crafted .aff/.dic files (xinyi234) Solution: Replace sprintf() with vim_snprintf() in spell_read_aff(). Replace STRCAT() with STRNCAT() with explicit remaining-space calculation in store_aff_word(). closes: #19944 Signed-off-by: Christian Brabandt --- diff --git a/src/spellfile.c b/src/spellfile.c index c570920795..a9a347a89a 100644 --- a/src/spellfile.c +++ b/src/spellfile.c @@ -2736,10 +2736,12 @@ spell_read_aff(spellinfo_T *spin, char_u *fname) char_u buf[MAXLINELEN]; aff_entry->ae_cond = getroom_save(spin, items[4]); + // Note: this silently truncates the buffer, but this should + // not happen in practice if (*items[0] == 'P') - sprintf((char *)buf, "^%s", items[4]); + vim_snprintf((char *)buf, sizeof(buf), "^%s", items[4]); else - sprintf((char *)buf, "%s$", items[4]); + vim_snprintf((char *)buf, sizeof(buf), "%s$", items[4]); aff_entry->ae_prog = vim_regcomp(buf, RE_MAGIC + RE_STRING + RE_STRICT); if (aff_entry->ae_prog == NULL) @@ -3906,7 +3908,9 @@ store_aff_word( else p += STRLEN(ae->ae_chop); } - STRCAT(newword, p); + // Note: this silently truncates the buffer, but this should + // not happen in practice + STRNCAT(newword, p, MAXWLEN - STRLEN(newword) - 1); } else { @@ -3922,7 +3926,9 @@ store_aff_word( *p = NUL; } if (ae->ae_add != NULL) - STRCAT(newword, ae->ae_add); + // Note: this silently truncates the buffer, but this should + // not happen in practice + STRNCAT(newword, ae->ae_add, MAXWLEN - STRLEN(newword) - 1); } use_flags = flags; diff --git a/src/testdir/test_spellfile.vim b/src/testdir/test_spellfile.vim index b72974ed07..f46a25d99e 100644 --- a/src/testdir/test_spellfile.vim +++ b/src/testdir/test_spellfile.vim @@ -1165,5 +1165,32 @@ func Test_mkspell_empty_dic() call delete('XtestEmpty.spl') endfunc +" This used to cause a buffer overflow +func Test_mkspell_no_buffer_overflow() + CheckNotMSWindows + + let aff_lines = ['SET ISO8859-1', 'SFX A Y 1', + \ 'SFX A 0 s ' .. repeat(nr2char(0xff), 491)] + call writefile(aff_lines, 'Xbof.aff', 'D') + call writefile(['1', 'word/A'], 'Xbof.dic', 'D') + " Must not crash; ignore any conversion/regex errors. + try + mkspell! Xbof.spl Xbof + catch + endtry + defer delete('Xbof.spl') + + let long = repeat(nr2char(0xff), 200) + let aff2_lines = ['SET ISO8859-1', 'SFX A Y 1', + \ 'SFX A 0 ' .. long .. ' .'] + call writefile(aff2_lines, 'Xbof2.aff', 'D') + call writefile(['1', long .. '/A'], 'Xbof2.dic', 'D') + try + mkspell! Xbof2.spl Xbof2 + catch + endtry + defer delete('Xbof2.spl') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 535e85ef85..ccffba42d0 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 331, /**/ 330, /**/