postgres.git / summary / log / commit / refs

commit    ea5f0d176a9d40df0ee6096203e1d1452f8db200
Author:   Tom Lane <tgl@sss.pgh.pa.us>
Date:     Wed Apr 22 16:02:15 2026 +0000
First released in: 14.23, 15.18, 16.14, 17.10, 18.4

    Prevent some buffer overruns in spell.c's parsing of affix files.
    
    parse_affentry() and addCompoundAffixFlagValue() each collect fields
    from an affix file into working buffers of size BUFSIZ.  They failed
    to defend against overlength fields, so that a malicious affix file
    could cause a stack smash.  BUFSIZ (typically 8K) is certainly way
    longer than any reasonable affix field, but let's fix this while
    we're closing holes in this area.
    
    I chose to do this by silently truncating the input before it can
    overrun the buffer, using logic comparable to the existing logic in
    get_nextfield().  Certainly there's at least as good an argument for
    raising an error, but for now let's follow the existing precedent.
    
    Reported-by: Igor Stepansky <igor.stepansky@orca.security>
    Author: Tom Lane <tgl@sss.pgh.pa.us>
    Reviewed-by: Andrey Borodin <x4mmm@yandex-team.ru>
    Discussion: https://postgr.es/m/864123.1776810909@sss.pgh.pa.us
    Backpatch-through: 14


src/backend/tsearch/spell.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/backend/tsearch/spell.c b/src/backend/tsearch/spell.c index a468356ce9b..5d96bd75319 100644 --- a/src/backend/tsearch/spell.c +++ b/src/backend/tsearch/spell.c @@ -908,14 +908,20 @@ parse_ooaffentry(char *str, char *type, char *flag, char *find, * * An .affix file entry has the following format: * <mask> > [-<find>,]<replace> + * + * Output buffers mask, find, repl must be of length BUFSIZ; + * we truncate the input to fit. */ static bool -parse_affentry(char *str, char *mask, char *find, char *repl) +parse_affentry(const char *str, char *mask, char *find, char *repl) { int state = PAE_WAIT_MASK; char *pmask = mask, *pfind = find, *prepl = repl; + char *emask = mask + BUFSIZ; + char *efind = find + BUFSIZ; + char *erepl = repl + BUFSIZ; *mask = *find = *repl = '\0'; @@ -929,7 +935,8 @@ parse_affentry(char *str, char *mask, char *find, char *repl) return false; else if (!t_isspace_cstr(str)) { - pmask += ts_copychar_with_len(pmask, str, clen); + if (pmask < emask - clen) + pmask += ts_copychar_with_len(pmask, str, clen); state = PAE_INMASK; } } @@ -942,7 +949,8 @@ parse_affentry(char *str, char *mask, char *find, char *repl) } else if (!t_isspace_cstr(str)) { - pmask += ts_copychar_with_len(pmask, str, clen); + if (pmask < emask - clen) + pmask += ts_copychar_with_len(pmask, str, clen); } } else if (state == PAE_WAIT_FIND) @@ -953,7 +961,8 @@ parse_affentry(char *str, char *mask, char *find, char *repl) } else if (t_isalpha_cstr(str) || t_iseq(str, '\'') /* english 's */ ) { - prepl += ts_copychar_with_len(prepl, str, clen); + if (prepl < erepl - clen) + prepl += ts_copychar_with_len(prepl, str, clen); state = PAE_INREPL; } else if (!t_isspace_cstr(str)) @@ -970,7 +979,8 @@ parse_affentry(char *str, char *mask, char *find, char *repl) } else if (t_isalpha_cstr(str)) { - pfind += ts_copychar_with_len(pfind, str, clen); + if (pfind < efind - clen) + pfind += ts_copychar_with_len(pfind, str, clen); } else if (!t_isspace_cstr(str)) ereport(ERROR, @@ -985,7 +995,8 @@ parse_affentry(char *str, char *mask, char *find, char *repl) } else if (t_isalpha_cstr(str)) { - prepl += ts_copychar_with_len(prepl, str, clen); + if (prepl < erepl - clen) + prepl += ts_copychar_with_len(prepl, str, clen); state = PAE_INREPL; } else if (!t_isspace_cstr(str)) @@ -1002,7 +1013,8 @@ parse_affentry(char *str, char *mask, char *find, char *repl) } else if (t_isalpha_cstr(str)) { - prepl += ts_copychar_with_len(prepl, str, clen); + if (prepl < erepl - clen) + prepl += ts_copychar_with_len(prepl, str, clen); } else if (!t_isspace_cstr(str)) ereport(ERROR, @@ -1060,7 +1072,7 @@ setCompoundAffixFlagValue(IspellDict *Conf, CompoundAffixFlag *entry, * val: affix parameter. */ static void -addCompoundAffixFlagValue(IspellDict *Conf, char *s, uint32 val) +addCompoundAffixFlagValue(IspellDict *Conf, const char *s, uint32 val) { CompoundAffixFlag *newValue; char sbuf[BUFSIZ]; @@ -1078,9 +1090,11 @@ addCompoundAffixFlagValue(IspellDict *Conf, char *s, uint32 val) sflag = sbuf; while (*s && !t_isspace_cstr(s) && *s != '\n') { - int clen = ts_copychar_cstr(sflag, s); + int clen = pg_mblen_cstr(s); - sflag += clen; + /* Truncate the input to fit in BUFSIZ */ + if (sflag < sbuf + BUFSIZ - clen) + sflag += ts_copychar_with_len(sflag, s, clen); s += clen; } *sflag = '\0'; [parent: a5426dbf8415]