mirror of
https://github.com/neovim/neovim.git
synced 2026-05-26 06:48:27 +00:00
vim-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: vim/vim#20286
9a920e8254
Co-authored-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
This commit is contained in:
@@ -542,6 +542,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.
|
||||
|
||||
@@ -224,6 +224,7 @@ EXTERN const char e_failed_to_find_all_diff_anchors[] INIT( = N_("E1550: Failed
|
||||
EXTERN const char e_diff_anchors_with_hidden_windows[] INIT( = N_("E1562: Diff anchors cannot be used with hidden diff windows"));
|
||||
EXTERN const char e_leadtab_requires_tab[] INIT( = N_("E1572: 'listchars' field \"leadtab\" requires \"tab\" to be specified"));
|
||||
EXTERN const char e_invalid_format_string_single_percent_s[] INIT( = N_("E1577: Invalid format string, only one \"%s\" is allowed"));
|
||||
EXTERN const char e_too_many_postponed_prefixes_spell[] INIT(= N_("E1578: Too many postponed prefixes and/or compound flags"));
|
||||
|
||||
EXTERN const char e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s"));
|
||||
EXTERN const char e_cannot_read_from_str_2[] INIT(= N_("E282: Cannot read from \"%s\""));
|
||||
|
||||
@@ -2929,6 +2929,19 @@ static void 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 *store_afflist, int *cntp, int id)
|
||||
{
|
||||
if (*cntp >= MAXWLEN - 1) {
|
||||
emsg(_(e_too_many_postponed_prefixes_spell));
|
||||
return FAIL;
|
||||
}
|
||||
store_afflist[(*cntp)++] = (char)(uint8_t)id;
|
||||
store_afflist[*cntp] = NUL;
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Returns true if flag "flag" appears in affix list "afflist".
|
||||
static bool flag_in_afflist(int flagtype, char *afflist, unsigned flag)
|
||||
{
|
||||
@@ -3180,6 +3193,7 @@ static int spell_read_dic(spellinfo_T *spin, char *fname, afffile_T *affile)
|
||||
int flags = 0;
|
||||
store_afflist[0] = NUL;
|
||||
int pfxlen = 0;
|
||||
int totlen = 0;
|
||||
bool need_affix = false;
|
||||
if (afflist != NULL) {
|
||||
// Extract flags from the affix list.
|
||||
@@ -3193,13 +3207,20 @@ static int spell_read_dic(spellinfo_T *spin, char *fname, afffile_T *affile)
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3279,13 +3300,12 @@ static int get_affix_flags(afffile_T *affile, char *afflist)
|
||||
return flags;
|
||||
}
|
||||
|
||||
// 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.
|
||||
static int get_pfxlist(afffile_T *affile, char *afflist, char *store_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.
|
||||
/// Returns FAIL when the fixed-size buffer would overflow.
|
||||
static int get_pfxlist(afffile_T *affile, char *afflist, char *store_afflist, int *cntp)
|
||||
{
|
||||
int cnt = 0;
|
||||
char key[AH_KEY_LEN];
|
||||
|
||||
for (char *p = afflist; *p != NUL;) {
|
||||
@@ -3297,8 +3317,8 @@ static int get_pfxlist(afffile_T *affile, char *afflist, char *store_afflist)
|
||||
hashitem_T *hi = hash_find(&affile->af_pref, key);
|
||||
if (!HASHITEM_EMPTY(hi)) {
|
||||
int id = HI2AH(hi)->ah_newID;
|
||||
if (id != 0) {
|
||||
store_afflist[cnt++] = (char)(uint8_t)id;
|
||||
if (id != 0 && store_afflist_add(store_afflist, cntp, id) == FAIL) {
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3307,16 +3327,15 @@ static int get_pfxlist(afffile_T *affile, char *afflist, char *store_afflist)
|
||||
}
|
||||
}
|
||||
|
||||
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[]".
|
||||
static void get_compflags(afffile_T *affile, char *afflist, char *store_afflist)
|
||||
/// 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 int get_compflags(afffile_T *affile, char *afflist, char *store_afflist, int *cntp)
|
||||
{
|
||||
int cnt = 0;
|
||||
char key[AH_KEY_LEN];
|
||||
|
||||
for (char *p = afflist; *p != NUL;) {
|
||||
@@ -3325,8 +3344,9 @@ static void get_compflags(afffile_T *affile, char *afflist, char *store_afflist)
|
||||
// A flag is a compound flag if it appears in "af_comp".
|
||||
xmemcpyz(key, prevp, (size_t)(p - prevp));
|
||||
hashitem_T *hi = hash_find(&affile->af_comp, key);
|
||||
if (!HASHITEM_EMPTY(hi)) {
|
||||
store_afflist[cnt++] = (char)(uint8_t)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 == ',') {
|
||||
@@ -3334,7 +3354,7 @@ static void get_compflags(afffile_T *affile, char *afflist, char *store_afflist)
|
||||
}
|
||||
}
|
||||
|
||||
store_afflist[cnt] = NUL;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/// Apply affixes to a word and store the resulting words.
|
||||
@@ -3466,9 +3486,16 @@ static int store_aff_word(spellinfo_T *spin, char *word, char *afflist, afffile_
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -3482,18 +3509,30 @@ static int store_aff_word(spellinfo_T *spin, char *word, char *afflist, afffile_
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
use_pfxlist[use_pfxlen] = NUL;
|
||||
}
|
||||
if (retval == FAIL) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Combine the list of compound flags.
|
||||
// Concatenate them to the prefix IDs list.
|
||||
@@ -3504,11 +3543,15 @@ static int store_aff_word(spellinfo_T *spin, char *word, char *afflist, afffile_
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (use_pfxlist[j] == NUL) {
|
||||
use_pfxlist[j++] = pfxlist[i];
|
||||
use_pfxlist[j] = NUL;
|
||||
if (use_pfxlist[j] == NUL
|
||||
&& store_afflist_add(use_pfxlist, &listlen, pfxlist[i]) == FAIL) {
|
||||
retval = FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (retval == FAIL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5497,7 +5540,9 @@ void spell_add_word(char *word, int len, SpellAddType what, int idx, bool undo)
|
||||
if (fpos_next < 0) {
|
||||
break; // should never happen
|
||||
}
|
||||
if (strncmp(word, line, (size_t)len) == 0
|
||||
size_t linelen = strlen(line);
|
||||
if (linelen >= (size_t)len
|
||||
&& strncmp(word, line, (size_t)len) == 0
|
||||
&& (line[len] == '/' || (uint8_t)line[len] < ' ')) {
|
||||
// Found duplicate word. Remove it by writing a '#' at
|
||||
// the start of the line. Mixing reading and writing
|
||||
|
||||
@@ -1219,5 +1219,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
|
||||
|
||||
Reference in New Issue
Block a user