From: Michael Paquier Date: Mon, 11 May 2026 12:13:46 +0000 (-0700) Subject: Fix overflows with ts_headline() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d388e1d7f0468db8d046f9101f972d1fa988b19a;p=thirdparty%2Fpostgresql.git 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 Backpatch-through: 14 Security: CVE-2026-6473 --- diff --git a/src/backend/tsearch/wparser_def.c b/src/backend/tsearch/wparser_def.c index 8b9b34e762a..480915030df 100644 --- a/src/backend/tsearch/wparser_def.c +++ b/src/backend/tsearch/wparser_def.c @@ -2583,6 +2583,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; @@ -2672,9 +2675,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 9287c440709..5b7c2123f37 100644 --- a/src/test/regress/expected/tsearch.out +++ b/src/test/regress/expected/tsearch.out @@ -2145,6 +2145,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 dc74aa0c889..8b3d700f57c 100644 --- a/src/test/regress/sql/tsearch.sql +++ b/src/test/regress/sql/tsearch.sql @@ -647,6 +647,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);