]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix overflows with ts_headline()
authorMichael Paquier <michael@paquier.xyz>
Mon, 11 May 2026 12:13:50 +0000 (05:13 -0700)
committerNoah Misch <noah@leadboat.com>
Mon, 11 May 2026 12:13:50 +0000 (05:13 -0700)
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
src/test/regress/expected/tsearch.out
src/test/regress/sql/tsearch.sql

index 10bd650671962c10f0af7de9cb68c02343c3bdfd..9ffa5cf24067a5327f8c23686c82518cf694a2f7 100644 (file)
@@ -2556,6 +2556,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;
@@ -2641,9 +2644,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);
 }
index 5d3d9d6250be2904800b23e7a0f8cca07c0aba65..6055ab9fbaab6c708fa620378208f40ee726c0c7 100644 (file)
@@ -2017,6 +2017,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
index c931f1a06f3a0caf688c6ca4a4d2724502e53edc..3bf9924c70561f40bd5f3f71b62bb44b2cbc4f6b 100644 (file)
@@ -569,6 +569,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);