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
This commit is contained in:
Michael Paquier 2026-05-11 05:13:49 -07:00 committed by Noah Misch
parent 6b6b26fdec
commit 5919e0005b
3 changed files with 39 additions and 3 deletions

View file

@ -2627,6 +2627,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;
@ -2716,9 +2719,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);
}

View file

@ -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

View file

@ -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);