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]