mirror of
https://github.com/vim/vim.git
synced 2026-05-28 00:21:37 +02:00
patch 9.2.0524: spell: buffer overflow with many affix or compound flags
Problem: spell: a word in a .dic file with many postponed prefix or
compound flags overflows the fixed-size store_afflist[MAXWLEN]
buffer in get_pfxlist() and get_compflags().
Solution: Add bounds checks (Yasuhiro Matsumoto).
closes: #20286
Signed-off-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
5e3056ee83
commit
9a920e8254
@@ -1,4 +1,4 @@
|
||||
*spell.txt* For Vim version 9.2. Last change: 2026 Feb 14
|
||||
*spell.txt* For Vim version 9.2. Last change: 2026 May 23
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -562,6 +562,11 @@ then Vim will try to guess.
|
||||
avoid running out of memory compression will be done
|
||||
now and then. This can be tuned with the 'mkspellmem'
|
||||
option.
|
||||
*E1578*
|
||||
There is a limit on how many postponed prefix and
|
||||
compound flags can be stored for one word. Reduce the
|
||||
number of affix/compound flags on a word in the .dic
|
||||
file that exceeds it.
|
||||
|
||||
After the spell file was written and it was being used
|
||||
in a buffer it will be reloaded automatically.
|
||||
|
||||
@@ -4776,6 +4776,7 @@ E1574 channel.txt /*E1574*
|
||||
E1575 builtin.txt /*E1575*
|
||||
E1576 tagsrch.txt /*E1576*
|
||||
E1577 options.txt /*E1577*
|
||||
E1578 spell.txt /*E1578*
|
||||
E158 sign.txt /*E158*
|
||||
E159 sign.txt /*E159*
|
||||
E16 cmdline.txt /*E16*
|
||||
|
||||
@@ -3812,3 +3812,7 @@ EXTERN char e_tag_file_entry_must_not_be_url[]
|
||||
INIT(= N_("E1576: Tag file entry must not be a URL"));
|
||||
EXTERN char e_invalid_format_string_single_percent_s[]
|
||||
INIT(= N_("E1577: Invalid format string, only one \"%s\" is allowed"));
|
||||
#ifdef FEAT_SPELL
|
||||
EXTERN char e_too_many_postponed_prefixes_spell[]
|
||||
INIT(= N_("E1578: Too many postponed prefixes and/or compound flags"));
|
||||
#endif
|
||||
|
||||
Generated
+4
-1
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Vim\n"
|
||||
"Report-Msgid-Bugs-To: vim-dev@vim.org\n"
|
||||
"POT-Creation-Date: 2026-05-21 19:38+0000\n"
|
||||
"POT-Creation-Date: 2026-05-23 19:49+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -8880,6 +8880,9 @@ msgstr ""
|
||||
msgid "E1577: Invalid format string, only one \"%s\" is allowed"
|
||||
msgstr ""
|
||||
|
||||
msgid "E1578: Too many postponed prefixes and/or compound flags"
|
||||
msgstr ""
|
||||
|
||||
#. type of cmdline window or 0
|
||||
#. result of cmdline window or 0
|
||||
#. buffer of cmdline window or NULL
|
||||
|
||||
+101
-28
@@ -2013,8 +2013,8 @@ static int str_equal(char_u *s1, char_u *s2);
|
||||
static void add_fromto(spellinfo_T *spin, garray_T *gap, char_u *from, char_u *to);
|
||||
static int sal_to_bool(char_u *s);
|
||||
static int get_affix_flags(afffile_T *affile, char_u *afflist);
|
||||
static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist);
|
||||
static void get_compflags(afffile_T *affile, char_u *afflist, char_u *store_afflist);
|
||||
static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist, int *cntp);
|
||||
static int get_compflags(afffile_T *affile, char_u *afflist, char_u *store_afflist, int *cntp);
|
||||
static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afffile_T *affile, hashtab_T *ht, hashtab_T *xht, int condit, int flags, char_u *pfxlist, int pfxlen);
|
||||
static void *getroom(spellinfo_T *spin, size_t len, int align);
|
||||
static char_u *getroom_save(spellinfo_T *spin, char_u *s);
|
||||
@@ -3357,6 +3357,26 @@ check_renumber(spellinfo_T *spin)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Append one affix or compound ID to "store_afflist".
|
||||
* Returns FAIL when this would overrun the fixed-size buffer.
|
||||
*/
|
||||
static int
|
||||
store_afflist_add(
|
||||
char_u *store_afflist,
|
||||
int *cntp,
|
||||
int id)
|
||||
{
|
||||
if (*cntp >= MAXWLEN - 1)
|
||||
{
|
||||
emsg(_(e_too_many_postponed_prefixes_spell));
|
||||
return FAIL;
|
||||
}
|
||||
store_afflist[(*cntp)++] = id;
|
||||
store_afflist[*cntp] = NUL;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TRUE if flag "flag" appears in affix list "afflist".
|
||||
*/
|
||||
@@ -3516,6 +3536,7 @@ spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
|
||||
char_u *afflist;
|
||||
char_u store_afflist[MAXWLEN];
|
||||
int pfxlen;
|
||||
int totlen;
|
||||
int need_affix;
|
||||
char_u *dw;
|
||||
char_u *pc;
|
||||
@@ -3665,6 +3686,7 @@ spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
|
||||
flags = 0;
|
||||
store_afflist[0] = NUL;
|
||||
pfxlen = 0;
|
||||
totlen = 0;
|
||||
need_affix = FALSE;
|
||||
if (afflist != NULL)
|
||||
{
|
||||
@@ -3676,13 +3698,28 @@ spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
|
||||
need_affix = TRUE;
|
||||
|
||||
if (affile->af_pfxpostpone)
|
||||
{
|
||||
// Need to store the list of prefix IDs with the word.
|
||||
pfxlen = get_pfxlist(affile, afflist, store_afflist);
|
||||
if (get_pfxlist(affile, afflist, store_afflist, &totlen)
|
||||
== FAIL)
|
||||
{
|
||||
retval = FAIL;
|
||||
break;
|
||||
}
|
||||
pfxlen = totlen;
|
||||
}
|
||||
|
||||
if (spin->si_compflags != NULL)
|
||||
{
|
||||
// Need to store the list of compound flags with the word.
|
||||
// Concatenate them to the list of prefix IDs.
|
||||
get_compflags(affile, afflist, store_afflist + pfxlen);
|
||||
if (get_compflags(affile, afflist, store_afflist, &totlen)
|
||||
== FAIL)
|
||||
{
|
||||
retval = FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the word to the word tree(s).
|
||||
@@ -3753,18 +3790,18 @@ get_affix_flags(afffile_T *affile, char_u *afflist)
|
||||
/*
|
||||
* Get the list of prefix IDs from the affix list "afflist".
|
||||
* Used for PFXPOSTPONE.
|
||||
* Put the resulting flags in "store_afflist[MAXWLEN]" with a terminating NUL
|
||||
* and return the number of affixes.
|
||||
* Put the resulting flags in "store_afflist[MAXWLEN]" with a terminating NUL.
|
||||
* Returns FAIL when the fixed-size buffer would overflow.
|
||||
*/
|
||||
static int
|
||||
get_pfxlist(
|
||||
afffile_T *affile,
|
||||
char_u *afflist,
|
||||
char_u *store_afflist)
|
||||
char_u *store_afflist,
|
||||
int *cntp)
|
||||
{
|
||||
char_u *p;
|
||||
char_u *prevp;
|
||||
int cnt = 0;
|
||||
int id;
|
||||
char_u key[AH_KEY_LEN];
|
||||
hashitem_T *hi;
|
||||
@@ -3781,32 +3818,32 @@ get_pfxlist(
|
||||
if (!HASHITEM_EMPTY(hi))
|
||||
{
|
||||
id = HI2AH(hi)->ah_newID;
|
||||
if (id != 0)
|
||||
store_afflist[cnt++] = id;
|
||||
if (id != 0 && store_afflist_add(store_afflist, cntp, id) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
if (affile->af_flagtype == AFT_NUM && *p == ',')
|
||||
++p;
|
||||
}
|
||||
|
||||
store_afflist[cnt] = NUL;
|
||||
return cnt;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the list of compound IDs from the affix list "afflist" that are used
|
||||
* for compound words.
|
||||
* Puts the flags in "store_afflist[]".
|
||||
* Returns FAIL when the fixed-size buffer would overflow.
|
||||
*/
|
||||
static void
|
||||
static int
|
||||
get_compflags(
|
||||
afffile_T *affile,
|
||||
char_u *afflist,
|
||||
char_u *store_afflist)
|
||||
char_u *store_afflist,
|
||||
int *cntp)
|
||||
{
|
||||
char_u *p;
|
||||
char_u *prevp;
|
||||
int cnt = 0;
|
||||
char_u key[AH_KEY_LEN];
|
||||
hashitem_T *hi;
|
||||
|
||||
@@ -3818,14 +3855,16 @@ get_compflags(
|
||||
// A flag is a compound flag if it appears in "af_comp".
|
||||
vim_strncpy(key, prevp, p - prevp);
|
||||
hi = hash_find(&affile->af_comp, key);
|
||||
if (!HASHITEM_EMPTY(hi))
|
||||
store_afflist[cnt++] = HI2CI(hi)->ci_newID;
|
||||
if (!HASHITEM_EMPTY(hi)
|
||||
&& store_afflist_add(store_afflist, cntp,
|
||||
HI2CI(hi)->ci_newID) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
if (affile->af_flagtype == AFT_NUM && *p == ',')
|
||||
++p;
|
||||
}
|
||||
|
||||
store_afflist[cnt] = NUL;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3983,10 +4022,20 @@ store_aff_word(
|
||||
if (affile->af_pfxpostpone
|
||||
|| spin->si_compflags != NULL)
|
||||
{
|
||||
int listlen = 0;
|
||||
|
||||
if (affile->af_pfxpostpone)
|
||||
{
|
||||
// Get prefix IDS from the affix list.
|
||||
use_pfxlen = get_pfxlist(affile,
|
||||
ae->ae_flags, store_afflist);
|
||||
if (get_pfxlist(affile, ae->ae_flags,
|
||||
store_afflist, &listlen)
|
||||
== FAIL)
|
||||
{
|
||||
retval = FAIL;
|
||||
break;
|
||||
}
|
||||
use_pfxlen = listlen;
|
||||
}
|
||||
else
|
||||
use_pfxlen = 0;
|
||||
use_pfxlist = store_afflist;
|
||||
@@ -3998,14 +4047,30 @@ store_aff_word(
|
||||
for (j = 0; j < use_pfxlen; ++j)
|
||||
if (pfxlist[i] == use_pfxlist[j])
|
||||
break;
|
||||
if (j == use_pfxlen)
|
||||
use_pfxlist[use_pfxlen++] = pfxlist[i];
|
||||
if (j == use_pfxlen
|
||||
&& store_afflist_add(use_pfxlist,
|
||||
&listlen, pfxlist[i])
|
||||
== FAIL)
|
||||
{
|
||||
retval = FAIL;
|
||||
break;
|
||||
}
|
||||
use_pfxlen = listlen;
|
||||
}
|
||||
if (retval == FAIL)
|
||||
break;
|
||||
|
||||
if (spin->si_compflags != NULL)
|
||||
// Get compound IDS from the affix list.
|
||||
get_compflags(affile, ae->ae_flags,
|
||||
use_pfxlist + use_pfxlen);
|
||||
if (get_compflags(affile, ae->ae_flags,
|
||||
use_pfxlist, &listlen)
|
||||
== FAIL)
|
||||
{
|
||||
retval = FAIL;
|
||||
break;
|
||||
}
|
||||
if (retval == FAIL)
|
||||
break;
|
||||
|
||||
// Combine the list of compound flags.
|
||||
// Concatenate them to the prefix IDs list.
|
||||
@@ -4016,12 +4081,17 @@ store_aff_word(
|
||||
use_pfxlist[j] != NUL; ++j)
|
||||
if (pfxlist[i] == use_pfxlist[j])
|
||||
break;
|
||||
if (use_pfxlist[j] == NUL)
|
||||
if (use_pfxlist[j] == NUL
|
||||
&& store_afflist_add(use_pfxlist,
|
||||
&listlen, pfxlist[i])
|
||||
== FAIL)
|
||||
{
|
||||
use_pfxlist[j++] = pfxlist[i];
|
||||
use_pfxlist[j] = NUL;
|
||||
retval = FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (retval == FAIL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6236,6 +6306,7 @@ spell_add_word(
|
||||
char_u line[MAXWLEN * 2];
|
||||
long fpos, fpos_next = 0;
|
||||
int i;
|
||||
size_t linelen;
|
||||
char_u *spf;
|
||||
|
||||
if (!valid_spell_word(word, word + len))
|
||||
@@ -6312,7 +6383,9 @@ spell_add_word(
|
||||
fpos_next = ftell(fd);
|
||||
if (fpos_next < 0)
|
||||
break; // should never happen
|
||||
if (STRNCMP(word, line, len) == 0
|
||||
linelen = STRLEN(line);
|
||||
if (linelen >= (size_t)len
|
||||
&& STRNCMP(word, line, len) == 0
|
||||
&& (line[len] == '/' || line[len] < ' '))
|
||||
{
|
||||
// Found duplicate word. Remove it by writing a '#' at
|
||||
|
||||
@@ -1214,5 +1214,37 @@ func Test_mkspell_no_buffer_overflow()
|
||||
defer delete('Xbof2.spl')
|
||||
endfunc
|
||||
|
||||
func Test_mkspell_no_affixlist_overflow()
|
||||
let aff_lines = [
|
||||
\ 'SET ISO8859-1',
|
||||
\ 'PFXPOSTPONE',
|
||||
\ 'PFX A Y 1',
|
||||
\ 'PFX A 0 pre .',
|
||||
\ ]
|
||||
call writefile(aff_lines, 'Xaffbof.aff', 'D')
|
||||
call writefile(['1', 'word/' .. repeat('A', 300)], 'Xaffbof.dic', 'D')
|
||||
|
||||
call assert_fails('mkspell! Xaffbof.spl Xaffbof',
|
||||
\ 'Too many postponed prefixes and/or compound flags')
|
||||
call assert_false(filereadable('Xaffbof.spl'))
|
||||
endfunc
|
||||
|
||||
func Test_mkspell_no_compflag_overflow()
|
||||
" Overflow the compound-flag path in get_compflags(): a word whose
|
||||
" affix list repeats a compound flag many times accumulates one ID per
|
||||
" occurrence, overrunning store_afflist[MAXWLEN].
|
||||
let aff_lines = [
|
||||
\ 'SET ISO8859-1',
|
||||
\ 'COMPOUNDFLAG c',
|
||||
\ ]
|
||||
call writefile(aff_lines, 'Xcompbof.aff', 'D')
|
||||
|
||||
" Repeat the compound flag 'c' far past MAXWLEN.
|
||||
call writefile(['1', 'word/' .. repeat('c', 300)], 'Xcompbof.dic', 'D')
|
||||
|
||||
call assert_fails('mkspell! Xcompbof.spl Xcompbof',
|
||||
\ 'Too many postponed prefixes and/or compound flags')
|
||||
call assert_false(filereadable('Xcompbof.spl'))
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
||||
@@ -729,6 +729,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
524,
|
||||
/**/
|
||||
523,
|
||||
/**/
|
||||
|
||||
Reference in New Issue
Block a user