Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wFJgX-004xt7-0z for pgsql-bugs@arkaria.postgresql.org; Tue, 21 Apr 2026 22:35:13 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wFJgW-00AgBl-0u for pgsql-bugs@arkaria.postgresql.org; Tue, 21 Apr 2026 22:35:12 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wFJgW-00AgBd-04 for pgsql-bugs@lists.postgresql.org; Tue, 21 Apr 2026 22:35:12 +0000 Received: from sss.pgh.pa.us ([68.162.161.243]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1wFJgT-000000029oS-2Qtj for pgsql-bugs@lists.postgresql.org; Tue, 21 Apr 2026 22:35:10 +0000 Received: from sss1.sss.pgh.pa.us (localhost [127.0.0.1]) by sss.pgh.pa.us (8.15.2/8.15.2) with ESMTP id 63LMZ9mr864124 for ; Tue, 21 Apr 2026 18:35:09 -0400 From: Tom Lane To: pgsql-bugs@lists.postgresql.org Subject: Re: Potential buffer overrun in spell.c's CheckAffix() In-reply-to: <641711.1776792744@sss.pgh.pa.us> References: <641711.1776792744@sss.pgh.pa.us> Comments: In-reply-to Tom Lane message dated "Tue, 21 Apr 2026 13:32:24 -0400" MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" Content-ID: <864013.1776810813.0@sss.pgh.pa.us> Date: Tue, 21 Apr 2026 18:35:09 -0400 Message-ID: <864123.1776810909@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk ------- =_aaaaaaaaaa0 Content-Type: text/plain; charset="us-ascii" Content-ID: <864013.1776810813.1@sss.pgh.pa.us> Further to that ... I found another item in the pgsql-security archives concerning a buffer overrun in ispell affix-file parsing, which we had likewise deemed not a security vulnerability because text search configuration files are assumed trustworthy. But if we're going to tighten up CheckAffix() then it's pretty silly not to fix these issues too. regards, tom lane ------- =_aaaaaaaaaa0 Content-Type: text/x-diff; name*0="v1-0001-Prevent-some-buffer-overruns-in-spell.c-s-parsing.p"; name*1="atch"; charset="us-ascii" Content-ID: <864013.1776810813.2@sss.pgh.pa.us> Content-Description: v1-0001-Prevent-some-buffer-overruns-in-spell.c-s-parsing.patch Content-Transfer-Encoding: quoted-printable =46rom 740e9b9887dc47d8f12745ade91839ffe27e40d2 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 21 Apr 2026 18:07:23 -0400 Subject: [PATCH v1] Prevent some buffer overruns in spell.c's parsing of a= ffix 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 Author: Tom Lane 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 a1bfd2a9f9b..dced5c444e0 100644 --- a/src/backend/tsearch/spell.c +++ b/src/backend/tsearch/spell.c @@ -909,14 +909,20 @@ parse_ooaffentry(char *str, char *type, char *flag, = char *find, * * An .affix file entry has the following format: * > [-,] + * + * 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 =3D PAE_WAIT_MASK; char *pmask =3D mask, *pfind =3D find, *prepl =3D repl; + char *emask =3D mask + BUFSIZ; + char *efind =3D find + BUFSIZ; + char *erepl =3D repl + BUFSIZ; = *mask =3D *find =3D *repl =3D '\0'; = @@ -930,7 +936,8 @@ parse_affentry(char *str, char *mask, char *find, char= *repl) return false; else if (!isspace((unsigned char) *str)) { - pmask +=3D ts_copychar_with_len(pmask, str, clen); + if (pmask < emask - clen) + pmask +=3D ts_copychar_with_len(pmask, str, clen); state =3D PAE_INMASK; } } @@ -943,7 +950,8 @@ parse_affentry(char *str, char *mask, char *find, char= *repl) } else if (!isspace((unsigned char) *str)) { - pmask +=3D ts_copychar_with_len(pmask, str, clen); + if (pmask < emask - clen) + pmask +=3D ts_copychar_with_len(pmask, str, clen); } } else if (state =3D=3D PAE_WAIT_FIND) @@ -954,7 +962,8 @@ parse_affentry(char *str, char *mask, char *find, char= *repl) } else if (t_isalpha_cstr(str) || t_iseq(str, '\'') /* english 's */ ) { - prepl +=3D ts_copychar_with_len(prepl, str, clen); + if (prepl < erepl - clen) + prepl +=3D ts_copychar_with_len(prepl, str, clen); state =3D PAE_INREPL; } else if (!isspace((unsigned char) *str)) @@ -971,7 +980,8 @@ parse_affentry(char *str, char *mask, char *find, char= *repl) } else if (t_isalpha_cstr(str)) { - pfind +=3D ts_copychar_with_len(pfind, str, clen); + if (pfind < efind - clen) + pfind +=3D ts_copychar_with_len(pfind, str, clen); } else if (!isspace((unsigned char) *str)) ereport(ERROR, @@ -986,7 +996,8 @@ parse_affentry(char *str, char *mask, char *find, char= *repl) } else if (t_isalpha_cstr(str)) { - prepl +=3D ts_copychar_with_len(prepl, str, clen); + if (prepl < erepl - clen) + prepl +=3D ts_copychar_with_len(prepl, str, clen); state =3D PAE_INREPL; } else if (!isspace((unsigned char) *str)) @@ -1003,7 +1014,8 @@ parse_affentry(char *str, char *mask, char *find, ch= ar *repl) } else if (t_isalpha_cstr(str)) { - prepl +=3D ts_copychar_with_len(prepl, str, clen); + if (prepl < erepl - clen) + prepl +=3D ts_copychar_with_len(prepl, str, clen); } else if (!isspace((unsigned char) *str)) ereport(ERROR, @@ -1061,7 +1073,7 @@ setCompoundAffixFlagValue(IspellDict *Conf, Compound= AffixFlag *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]; @@ -1079,9 +1091,11 @@ addCompoundAffixFlagValue(IspellDict *Conf, char *s= , uint32 val) sflag =3D sbuf; while (*s && !isspace((unsigned char) *s) && *s !=3D '\n') { - int clen =3D ts_copychar_cstr(sflag, s); + int clen =3D pg_mblen_cstr(s); = - sflag +=3D clen; + /* Truncate the input to fit in BUFSIZ */ + if (sflag < sbuf + BUFSIZ - clen) + sflag +=3D ts_copychar_with_len(sflag, s, clen); s +=3D clen; } *sflag =3D '\0'; -- = 2.43.7 ------- =_aaaaaaaaaa0--