mirror of
https://github.com/neovim/neovim.git
synced 2026-05-23 21:30:11 +00:00
vim-patch:9.2.0450: [security]: heap buffer overflow in spellfile.c read_compound() (#39660)
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
9299332917
Co-authored-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -331,6 +331,9 @@ enum {
|
||||
CF_UPPER = 0x02,
|
||||
};
|
||||
|
||||
// Max allowed length for COMPOUND section
|
||||
#define COMPOUND_MAX_LEN 100000
|
||||
|
||||
static const char *e_spell_trunc = N_("E758: Truncated spell file");
|
||||
static const char e_error_while_reading_sug_file_str[]
|
||||
= N_("E782: Error while reading .sug file: %s");
|
||||
@@ -1431,24 +1434,28 @@ static int 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;
|
||||
c += todo * 2;
|
||||
char *pat = xmalloc((size_t)c);
|
||||
if ((size_t)todo > COMPOUND_MAX_LEN) {
|
||||
return SP_FORMERROR;
|
||||
}
|
||||
size_t patsize = (size_t)todo * 2 + 7;
|
||||
patsize += (size_t)todo * 2;
|
||||
size_t flagsize = (size_t)todo + 1;
|
||||
char *pat = xmalloc(patsize);
|
||||
|
||||
// We also need a list of all flags that can appear at the start and one
|
||||
// for all flags.
|
||||
uint8_t *cp = xmalloc((size_t)todo + 1);
|
||||
uint8_t *cp = xmalloc(flagsize);
|
||||
slang->sl_compstartflags = cp;
|
||||
*cp = NUL;
|
||||
|
||||
uint8_t *ap = xmalloc((size_t)todo + 1);
|
||||
uint8_t *ap = xmalloc(flagsize);
|
||||
slang->sl_compallflags = ap;
|
||||
*ap = NUL;
|
||||
|
||||
// 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.
|
||||
uint8_t *crp = xmalloc((size_t)todo + 1);
|
||||
uint8_t *crp = xmalloc(flagsize);
|
||||
slang->sl_comprules = crp;
|
||||
|
||||
char *pp = pat;
|
||||
|
||||
@@ -337,6 +337,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, '')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user