postgres.git / summary / log / commit / refs
commit 3ed3dbbf440a81590ade81ac6553dc7380a26561
Author: Michael Paquier <michael@paquier.xyz>
Commit: Noah Misch <noah@leadboat.com>
Date: Mon May 11 12:13:48 2026 +0000
First released in: 14.23, 15.18, 16.14, 17.10, 18.4
Fix overflows with ts_headline()
The options "StartSel", "StopSel" and "FragmentDelimiter" given by a
caller of the SQL function ts_headline() have their lengths stored as
int16. When providing values larger than PG_INT16_MAX, it was possible
to overflow the length values stored, leading to incorrect behaviors in
generateHeadline(), in most cases translating to a crash.
Attempting to use values for these options larger than PG_INT16_MAX is
now blocked. Some test cases are added to cover our tracks.
Reported-by: Xint Code
Author: Michael Paquier <michael@paquier.xyz>
Backpatch-through: 14
Security: CVE-2026-6473
src/backend/tsearch/wparser_def.c | 24 +++++++++++++++++++++---
src/test/regress/expected/tsearch.out | 10 ++++++++++
src/test/regress/sql/tsearch.sql | 8 ++++++++
3 files changed, 39 insertions(+), 3 deletions(-)
diff --git a/src/backend/tsearch/wparser_def.c b/src/backend/tsearch/wparser_def.c
index 2b80156430a..99e4ee73c89 100644
--- a/src/backend/tsearch/wparser_def.c
+++ b/src/backend/tsearch/wparser_def.c
@@ -2628,6 +2628,9 @@ prsd_headline(PG_FUNCTION_ARGS)
int max_fragments = 0;
bool highlightall = false;
ListCell *l;
+ size_t startsellen;
+ size_t stopsellen;
+ size_t fragdelimlen;
/* Extract configuration option values */
prs->startsel = NULL;
@@ -2717,9 +2720,24 @@ prsd_headline(PG_FUNCTION_ARGS)
prs->fragdelim = pstrdup(" ... ");
/* Caller will need these lengths, too */
- prs->startsellen = strlen(prs->startsel);
- prs->stopsellen = strlen(prs->stopsel);
- prs->fragdelimlen = strlen(prs->fragdelim);
+ startsellen = strlen(prs->startsel);
+ stopsellen = strlen(prs->stopsel);
+ fragdelimlen = strlen(prs->fragdelim);
+ if (startsellen > PG_INT16_MAX)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("value for \"%s\" is too long", "StartSel")));
+ if (stopsellen > PG_INT16_MAX)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("value for \"%s\" is too long", "StopSel")));
+ if (fragdelimlen > PG_INT16_MAX)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("value for \"%s\" is too long", "FragmentDelimiter")));
+ prs->startsellen = startsellen;
+ prs->stopsellen = stopsellen;
+ prs->fragdelimlen = fragdelimlen;
PG_RETURN_POINTER(prs);
}
diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out
index 9fad6c8b04b..38ddb7aa64e 100644
--- a/src/test/regress/expected/tsearch.out
+++ b/src/test/regress/expected/tsearch.out
@@ -2144,6 +2144,16 @@ NOTICE: text-search query doesn't contain lexemes: ""
foo bar
(1 row)
+-- Test for large values of StartSel, StopSel and FragmentDelimiter
+SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'),
+ 'StartSel=' || repeat('x', 32768));
+ERROR: value for "StartSel" is too long
+SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'),
+ 'StopSel=' || repeat('x', 32768));
+ERROR: value for "StopSel" is too long
+SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'),
+ 'FragmentDelimiter=' || repeat('x', 32768));
+ERROR: value for "FragmentDelimiter" is too long
--Rewrite sub system
CREATE TABLE test_tsquery (txtkeyword TEXT, txtsample TEXT);
\set ECHO none
diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql
index fbd26cdba45..bcaff947dca 100644
--- a/src/test/regress/sql/tsearch.sql
+++ b/src/test/regress/sql/tsearch.sql
@@ -646,6 +646,14 @@ SELECT ts_headline('english',
SELECT ts_headline('english',
'foo bar', to_tsquery('english', ''));
+-- Test for large values of StartSel, StopSel and FragmentDelimiter
+SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'),
+ 'StartSel=' || repeat('x', 32768));
+SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'),
+ 'StopSel=' || repeat('x', 32768));
+SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'),
+ 'FragmentDelimiter=' || repeat('x', 32768));
+
--Rewrite sub system
CREATE TABLE test_tsquery (txtkeyword TEXT, txtsample TEXT);
[parent: 8c3426110934]