]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0450: [security]: heap buffer overflow in spellfile.c read_compound() v9.2.0450
authorChristian Brabandt <cb@256bit.org>
Wed, 6 May 2026 18:50:00 +0000 (20:50 +0200)
committerChristian Brabandt <cb@256bit.org>
Thu, 7 May 2026 18:49:53 +0000 (18:49 +0000)
Problem:  read_compound() in spellfile.c computes the size of the regex
          pattern buffer using signed-int arithmetic on the attacker
          controlled SN_COMPOUND sectionlen.  With sectionlen=0x40000008
          and UTF-8 encoding active the multiplication wraps to 27 while
          the per-byte loop writes up to ~1B bytes, overflowing the heap.
          Reachable when loading a crafted .spl file (e.g. via 'set spell'
          after a modeline sets 'spelllang').  The cp/ap/crp allocations
          have the same int + 1 overflow class (Daniel Cervera)
Solution: Use type size_t as buffer size and reject values larger than
          COMPOUND_MAX_LEN (100000).  Apply the same size_t treatment to
          the cp/ap/crp allocations.

Github Advisory:
https://github.com/vim/vim/security/advisories/GHSA-q4jv-r9gj-6cwv

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/spellfile.c
src/testdir/test_spellfile.vim
src/version.c

index a9a347a89a97856ade981267a0f5e71bd67ba46a..5102dad5b6da2b8094c5035ae5cefc5a1057e1d9 100644 (file)
 #define CF_WORD                0x01
 #define CF_UPPER       0x02
 
+// Max allowed length for COMPOUND section
+#define COMPOUND_MAX_LEN       100000
+
 /*
  * Loop through all the siblings of a node (including the node)
  */
@@ -1219,6 +1222,8 @@ read_compound(FILE *fd, slang_T *slang, int len)
     char_u     *crp;
     int                cnt;
     garray_T   *gap;
+    size_t     patsize;
+    size_t     flagsize;
 
     if (todo < 2)
        return SP_FORMERROR;    // need at least two bytes
@@ -1275,16 +1280,19 @@ read_compound(FILE *fd, slang_T *slang, int len)
     // "a[bc]/a*b+" -> "^\(a[bc]\|a*b\+\)$".
     // Inserting backslashes may double the length, "^\(\)$<Nul>" is 7 bytes.
     // Conversion to utf-8 may double the size.
-    c = todo * 2 + 7;
+    if ((size_t)todo > COMPOUND_MAX_LEN)
+       return SP_FORMERROR;
+    patsize = (size_t)todo * 2 + 7;
     if (enc_utf8)
-       c += todo * 2;
-    pat = alloc(c);
+       patsize += (size_t)todo * 2;
+    flagsize = (size_t)todo + 1;
+    pat = alloc(patsize);
     if (pat == NULL)
        return SP_OTHERERROR;
 
     // We also need a list of all flags that can appear at the start and one
     // for all flags.
-    cp = alloc(todo + 1);
+    cp = alloc(flagsize);
     if (cp == NULL)
     {
        vim_free(pat);
@@ -1293,7 +1301,7 @@ read_compound(FILE *fd, slang_T *slang, int len)
     slang->sl_compstartflags = cp;
     *cp = NUL;
 
-    ap = alloc(todo + 1);
+    ap = alloc(flagsize);
     if (ap == NULL)
     {
        vim_free(pat);
@@ -1305,7 +1313,7 @@ read_compound(FILE *fd, slang_T *slang, int len)
     // And a list of all patterns in their original form, for checking whether
     // compounding may work in match_compoundrule().  This is freed when we
     // encounter a wildcard, the check doesn't work then.
-    crp = alloc(todo + 1);
+    crp = alloc(flagsize);
     slang->sl_comprules = crp;
 
     pp = pat;
index f46a25d99e1c43843223e8f3781d5f6c8c2ab1da..8f3ef4907d57958a285921f7f653197d51211d9a 100644 (file)
@@ -334,6 +334,10 @@ func Test_spellfile_format_error()
   " SN_COMPOUND: incorrect comppatlen
   call Spellfile_Test(0z080000000007040101000000020165, 'E758:')
 
+  " SN_COMPOUND: oversized sectionlen
+  let v = eval('0z08004000000803010161' .. repeat('61', 50) .. 'FF')
+  call Spellfile_Test(v, 'E759:')
+
   " SN_INFO: missing info
   call Spellfile_Test(0z0F0000000005040101, '')
 
index 3cbce87d872b622b275ed53d13e9d1e7b69010ed..ff0960c2277b6f18aaf2cf21dc6e36f035023b7f 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    450,
 /**/
     449,
 /**/