From ba0e5b25bcaf307019b619db25bd19c78bb59846 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 25 Jul 2026 07:00:11 +0800 Subject: [PATCH 1/2] vim-patch:9.2.0846: [security]: heap buffer overflow in set_sofo() Problem: [security]: heap buffer overflow in set_sofo() (Yazan Balawneh) Solution: Reset sl_sal_first (Yasuhiro Matsumoto). A crafted spell file with an empty SN_SAL section before an SN_SOFO section reaches set_sofo() with sl_sal_first[] already set to -1 by set_sal_first(). The counting loop then under-counts colliding multi-byte "from" characters, allocates an undersized list and writes past its end. Github Security Advisory: https://github.com/vim/vim/security/advisories/GHSA-9jqx-hgpr-6v64 https://github.com/vim/vim/commit/05c41c922309c7a11b6ec2f124be66551c90d66a Co-authored-by: Yasuhiro Matsumoto --- src/nvim/spellfile.c | 4 +++- test/old/testdir/test_spellfile.vim | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 466aaf4578..f934b9f94e 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -1553,7 +1553,9 @@ static int set_sofo(slang_T *lp, const char *from, const char *to) lp->sl_sofo = true; // First count the number of items for each list. Temporarily use - // sl_sal_first[] for this. + // sl_sal_first[] for this. Reset it first: a preceding SN_SAL section + // may have set the entries to -1 via set_sal_first(). + memset(lp->sl_sal_first, 0, sizeof(salfirst_T) * 256); for (p = from, s = to; *p != NUL && *s != NUL;) { const int c = mb_cptr2char_adv(&p); s += utf_ptr2len(s); diff --git a/test/old/testdir/test_spellfile.vim b/test/old/testdir/test_spellfile.vim index 0938e4cfd1..9919821a6f 100644 --- a/test/old/testdir/test_spellfile.vim +++ b/test/old/testdir/test_spellfile.vim @@ -322,6 +322,11 @@ func Test_spellfile_format_error() " SN_SOFO: multi-byte characters in sofofrom and sofoto call Spellfile_Test(0z0600000000080002CF810002CF82FF000000000000000000000000, '') + " SN_SAL (empty) followed by SN_SOFO with two multi-byte 'from' characters + " sharing the same low byte. A preceding SN_SAL poisons sl_sal_first[], so + " without a reset set_sofo() under-counts and writes out of bounds. + call Spellfile_Test(0z05000000000300000006000000000A0004CAABCEAB00024142FF000000000000000000000000, '') + " SN_COMPOUND: compmax is less than 2 call Spellfile_Test(0z08000000000101, 'E759:') From 89cab37358a49f7bfe7f3d40b75f8b3a4a573222 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 25 Jul 2026 07:01:02 +0800 Subject: [PATCH 2/2] vim-patch:9.2.0854: memory leak when reading a spell file with SN_SAL and SN_SOFO Problem: Memory leak when a spell file has an SN_SAL section before an SN_SOFO section: set_sofo() reuses sl_sal without freeing the salitem_T entries left by read_sal_section() (after v9.2.0846). Solution: Factor the SAL free loop into free_sal_items() and call it before set_sofo() reuses sl_sal. closes: vim/vim#20836 Supported by AI. https://github.com/vim/vim/commit/5a7ce2733a2d9d9fccad4dd48047546000d9994d Co-authored-by: Christian Brabandt --- src/nvim/spell.c | 11 +++++++++-- src/nvim/spellfile.c | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 47bff07aae..af13135908 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -1678,6 +1678,14 @@ static void free_salitem(salitem_T *smp) xfree(smp->sm_to_w); } +/// Free the salitem_T entries in a "sl_sal" garray (the SN_SAL form) and +/// clear the garray. Used by slang_clear() and when set_sofo() reuses +/// sl_sal for the SN_SOFO form. +void free_sal_items(garray_T *gap) +{ + GA_DEEP_CLEAR(gap, salitem_T, free_salitem); +} + /// Frees a fromto_T static void free_fromto(fromto_T *ftp) { @@ -1706,8 +1714,7 @@ void slang_clear(slang_T *lp) // "ga_len" is set to 1 without adding an item for latin1 GA_DEEP_CLEAR_PTR(gap); } else { - // SAL items: free salitem_T items - GA_DEEP_CLEAR(gap, salitem_T, free_salitem); + free_sal_items(gap); } for (int i = 0; i < lp->sl_prefixcnt; i++) { diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index f934b9f94e..d9d35331fe 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -1541,6 +1541,8 @@ static int set_sofo(slang_T *lp, const char *from, const char *to) const char *s; const char *p; + free_sal_items(&lp->sl_sal); + // Use "sl_sal" as an array with 256 pointers to a list of wide // characters. The index is the low byte of the character. // The list contains from-to pairs with a terminating NUL.