From bce3bf06a30afa309722b7d212b2e100a75b4425 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 18 Aug 2026 09:03:37 +0800 Subject: [PATCH] vim-patch:9.2.0963: crash when sound-folding a crafted spell file (#41358) Problem: A SAL rule longer than MAXWLEN is silently truncated to an empty lead. set_sal_first() then reorders the sl_sal entries by their index byte and can move the terminating sentinel out of the last slot, so spell_soundfold_wsal() reads past the end of the array, e.g. when soundfold() or spellsuggest() is used (Erick Alex). Solution: Bound the sound-folding loops against sl_sal.ga_len. closes: vim/vim#21076 https://github.com/vim/vim/commit/6ac008db969677304ba888854e5d44a32ce79853 Co-authored-by: Christian Brabandt Co-authored-by: Claude Opus 4.8 (1M context) --- src/nvim/spell.c | 3 ++- test/old/testdir/test_spell_utf8.vim | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 69e7ae446c..8e6342de41 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2938,7 +2938,8 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) // Check all rules for the same index byte. // If c is 0x300 need extra check for the end of the array, as // (c & 0xff) is NUL. - for (; ((ws = smp[n].sm_lead_w)[0] & 0xff) == (c & 0xff) + for (; n < slang->sl_sal.ga_len + && ((ws = smp[n].sm_lead_w)[0] & 0xff) == (c & 0xff) && ws[0] != NUL; n++) { // Quickly skip entries that don't match the word. Most // entries are less than three chars, optimize for that. diff --git a/test/old/testdir/test_spell_utf8.vim b/test/old/testdir/test_spell_utf8.vim index f4ad23fa7b..a036ea97c6 100644 --- a/test/old/testdir/test_spell_utf8.vim +++ b/test/old/testdir/test_spell_utf8.vim @@ -828,5 +828,18 @@ func Test_spell_suggest_too_long() bwipe! endfunc +" A SAL rule that is too long to case-fold must not move the sentinel entry +" out of its last position in the sl_sal array. +func Test_spellfile_long_sal_rule() + call writefile(['1', 'ab'], 'Xlongsal.dic', 'D') + call writefile(['SAL ' .. repeat('w', 299) .. ' a', + \ "SAL a\u00e9 a"], 'Xlongsal.aff', 'D') + mkspell! Xlongsal Xlongsal + set spelllang=Xlongsal.utf-8.spl spell + " must not crash + call soundfold('ab') + set spelllang& spell& + call delete('Xlongsal.utf-8.spl') +endfunc " vim: shiftwidth=2 sts=2 expandtab