This is an automated email from the ASF dual-hosted git repository.

reshke pushed a commit to branch backport_cve
in repository https://gitbox.apache.org/repos/asf/cloudberry.git

commit 7d2170ad088f54d697b35f1437e7cdc047e1c4f2
Author: Tom Lane <[email protected]>
AuthorDate: Mon Aug 10 06:38:23 2026 -0700

    Harden tsquery code against overflows.
    
    The only overflow hazards I could find in tsquery construction
    are in QTN2QT(), which builds a flat tsquery datum from the
    QTNode tree representation used by tsquery_or, tsquery_rewrite,
    and allied functions.  There are two:
    
    1. It seems theoretically possible for the outputs of cntsize() to
    overflow an int, so I widened them to size_t.  There's no hazard
    certainly in tsquery_or and friends, but tsquery_rewrite could expand
    the query tree by large multiples (by replacing many identical
    subtrees with a large replacement tree), so in a 64-bit machine
    with plenty of available memory it should be possible to build a
    QTNode tree large enough to cause that.  If these counters did
    overflow then we'd under-allocate the output tsquery and have a heap
    overwrite problem.  size_t is sufficient, since it's counting the size
    of a subset of an in-memory data structure.  We also have to fix the
    TSQUERY_TOO_BIG() macro to not get confused if sumlen exceeds
    MaxAllocSize.
    
    2. fillQT() neglects to check that the new "distance" value for a
    QI_VAL item fits into the available 20-bit field.  It's quite easy
    to reach this, for example by tsquery_or'ing two near-megabyte-sized
    tsquerys.  However, the result is only a corrupt tsquery that does
    not represent the expected query, so perhaps this doesn't rise to
    the level of a security bug.  Nonetheless it should be fixed.
    
    Note: I followed the practice used in other tsquery code of checking
    each distance value as it's assigned, which means that the last
    operand string could extend past the MAXSTRPOS boundary.  This is a
    bit different from the pattern used for tsvectors, which insist that
    the total data length not exceed MAXSTRPOS and thereby avoid making
    per-item checks.  Perhaps that should be harmonized sometime, but for
    now it's okay for the two types to do this differently as long as
    each one is self-consistent.
    
    Author: Tom Lane <[email protected]>
    Reviewed-by: Amit Langote <[email protected]>
    Backpatch-through: 14
    Security: CVE-2026-14662
---
 src/backend/utils/adt/tsquery_util.c | 13 ++++++++++---
 src/include/tsearch/ts_type.h        |  3 ++-
 2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/src/backend/utils/adt/tsquery_util.c 
b/src/backend/utils/adt/tsquery_util.c
index 7b6970a6f82..9eabaf75cfe 100644
--- a/src/backend/utils/adt/tsquery_util.c
+++ b/src/backend/utils/adt/tsquery_util.c
@@ -289,7 +289,7 @@ QTNBinary(QTNode *in)
  * Caller must initialize *sumlen and *nnode to zeroes.
  */
 static void
-cntsize(QTNode *in, int *sumlen, int *nnode)
+cntsize(QTNode *in, size_t *sumlen, size_t *nnode)
 {
        /* since this function recurses, it could be driven to stack overflow. 
*/
        check_stack_depth();
@@ -327,10 +327,17 @@ fillQT(QTN2QTState *state, QTNode *in)
 
        if (in->valnode->type == QI_VAL)
        {
+               size_t          distance;
+
                memcpy(state->curitem, in->valnode, sizeof(QueryOperand));
 
                memcpy(state->curoperand, in->word, 
in->valnode->qoperand.length);
-               state->curitem->qoperand.distance = state->curoperand - 
state->operand;
+               distance = state->curoperand - state->operand;
+               if (distance > MAXSTRPOS)
+                       ereport(ERROR,
+                                       
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                        errmsg("tsquery is too large")));
+               state->curitem->qoperand.distance = distance;
                state->curoperand[in->valnode->qoperand.length] = '\0';
                state->curoperand += in->valnode->qoperand.length + 1;
                state->curitem++;
@@ -364,7 +371,7 @@ QTN2QT(QTNode *in)
 {
        TSQuery         out;
        int                     len;
-       int                     sumlen = 0,
+       size_t          sumlen = 0,
                                nnode = 0;
        QTN2QTState state;
 
diff --git a/src/include/tsearch/ts_type.h b/src/include/tsearch/ts_type.h
index 23f6a7ec593..5bfae27e32e 100644
--- a/src/include/tsearch/ts_type.h
+++ b/src/include/tsearch/ts_type.h
@@ -250,7 +250,8 @@ typedef TSQueryData *TSQuery;
  */
 #define COMPUTESIZE(size, lenofoperand) ( HDRSIZETQ + (size) * 
sizeof(QueryItem) + (lenofoperand) )
 #define TSQUERY_TOO_BIG(size, lenofoperand) \
-       ((size) > (MaxAllocSize - HDRSIZETQ - (lenofoperand)) / 
sizeof(QueryItem))
+       ((size_t) (lenofoperand) > MaxAllocSize - HDRSIZETQ || \
+        (size) > (MaxAllocSize - HDRSIZETQ - (lenofoperand)) / 
sizeof(QueryItem))
 
 /* Returns a pointer to the first QueryItem in a TSQuery */
 #define GETQUERY(x)  ((QueryItem*)( (char*)(x)+HDRSIZETQ ))


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to