postgres.git / summary / log / commit / refs

commit    2d267ffc449ad9e8a1fbc2e23d4b55754a5a6e02
Author:   Michael Paquier <michael@paquier.xyz>
Commit:   Noah Misch <noah@leadboat.com>
Date:     Mon May 11 12:13:51 2026 +0000

    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 48f9a87523c..8ccee5883dc 100644 --- a/src/backend/tsearch/wparser_def.c +++ b/src/backend/tsearch/wparser_def.c @@ -2557,6 +2557,9 @@ prsd_headline(PG_FUNCTION_ARGS) bool highlightall = false; int max_cover; ListCell *l; + size_t startsellen; + size_t stopsellen; + size_t fragdelimlen; /* Extract configuration option values */ prs->startsel = NULL; @@ -2642,9 +2645,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 629147c5b11..3204be7c60a 100644 --- a/src/test/regress/expected/tsearch.out +++ b/src/test/regress/expected/tsearch.out @@ -2007,6 +2007,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 0a90c1b539d..561656a6e57 100644 --- a/src/test/regress/sql/tsearch.sql +++ b/src/test/regress/sql/tsearch.sql @@ -555,6 +555,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: b545c3787671]