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 918078b013baa4277faa15ee63b6c840f72aed9a
Author: Tom Lane <[email protected]>
AuthorDate: Mon Aug 10 06:38:23 2026 -0700

    Protect some fixed-size arrays that have FUNC_MAX_ARGS elements.
    
    The maximum number of arguments allowed for an aggregate function
    is FUNC_MAX_ARGS-1 (since the underlying transfn and/or finalfn
    will be called with one more argument).  parse_func.c failed to
    enforce this, allowing construction of calls that would try to
    pass FUNC_MAX_ARGS+1 to the underlying functions, resulting in
    a memory stomp in the executor.  Add correct checking there.
    
    Since it's possible that a bad call has been stored in a view or
    SQL function, also add checks in various aggregate-related and
    window-function-related code that there are not more than
    FUNC_MAX_ARGS arguments.  These will also protect us against the
    possibility that we're trying to run a stored view that was made
    by a server executable with different FUNC_MAX_ARGS.  (Arguably,
    that scenario does not qualify as a security problem.  But let's
    just tighten up all of this while we're here, rather than split
    hairs over whether an overrun is reachable.)
    
    Likewise check in compute_function_hashkey.  Here the hazard is
    directly from a pg_proc row, but the scenario is the same.
    
    PL/Tcl has a similar issue with a fixed-size string buffer.
    Let's just replace that buffer with a Tcl_DString, removing the
    whole issue and making the code look more like what's around it.
    
    There are a lot of other FUNC_MAX_ARGS-sized arrays, but the rest
    have nearby guards already, some with comments explicitly pointing
    out the hazard of FUNC_MAX_ARGS changing.
    
    I also used palloc_array() in a few related places in funcapi.c.
    Those aren't live hazards AFAICS, but nearby code has been
    palloc_array-ified already, so it seemed inconsistent to not use
    it here.
    
    Reported-by: Masahiko Sawada <[email protected]>
    Author: Tom Lane <[email protected]>
    Reviewed-by: Masahiko Sawada <[email protected]>
    Backpatch-through: 14
    Security: CVE-2026-14679
---
 src/backend/executor/nodeWindowAgg.c | 33 +++++++++++++++++++++++++++++++++
 src/backend/parser/parse_agg.c       | 18 +++++++++++++++++-
 src/backend/parser/parse_func.c      | 29 +++++++++++++++++++++++++++++
 src/backend/utils/fmgr/funcapi.c     |  6 +++---
 src/pl/plpgsql/src/pl_comp.c         | 14 ++++++++++++++
 src/pl/tcl/pltcl.c                   | 22 +++++++++++++---------
 6 files changed, 109 insertions(+), 13 deletions(-)

diff --git a/src/backend/executor/nodeWindowAgg.c 
b/src/backend/executor/nodeWindowAgg.c
index 9ceb1fb5377..f0dd67ca7ee 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1295,6 +1295,20 @@ eval_windowfunction(WindowAggState *winstate, 
WindowStatePerFunc perfuncstate,
 
        oldContext = 
MemoryContextSwitchTo(winstate->ss.ps.ps_ExprContext->ecxt_per_tuple_memory);
 
+       /*
+        * Protect fixed-size fcinfo.  Ordinarily this would have been checked
+        * while creating the WindowFunc, but it's possible that we are looking 
at
+        * a parsetree from a stored view that was made by a server executable
+        * with a different value of FUNC_MAX_ARGS.
+        */
+       if (perfuncstate->numArguments > FUNC_MAX_ARGS)
+               ereport(ERROR,
+                               (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+                                errmsg_plural("cannot pass more than %d 
argument to a function",
+                                                          "cannot pass more 
than %d arguments to a function",
+                                                          FUNC_MAX_ARGS,
+                                                          FUNC_MAX_ARGS)));
+
        /*
         * We don't pass any normal arguments to a window function, but we do 
pass
         * it the number of arguments, in order to permit window function
@@ -3128,6 +3142,25 @@ initialize_peragg(WindowAggState *winstate, WindowFunc 
*wfunc,
 
        numArguments = list_length(wfunc->args);
 
+       /*
+        * Check the number of arguments, to protect fixed-size arrays here and
+        * later in node execution.
+        *
+        * Aggregates can have at most FUNC_MAX_ARGS-1 args (compare
+        * AggregateCreate, whose error message we want to match).  Ordinarily
+        * this would have been checked while creating the WindowFunc, but it's
+        * possible that we are looking at a parsetree from a stored view that 
was
+        * made by a server executable with a different value of FUNC_MAX_ARGS, 
or
+        * an executable in which parse_func.c didn't enforce the correct limit.
+        */
+       if (numArguments > FUNC_MAX_ARGS - 1)
+               ereport(ERROR,
+                               (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+                                errmsg_plural("aggregates cannot have more 
than %d argument",
+                                                          "aggregates cannot 
have more than %d arguments",
+                                                          FUNC_MAX_ARGS - 1,
+                                                          FUNC_MAX_ARGS - 1)));
+
        i = 0;
        foreach(lc, wfunc->args)
        {
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 1669ef066b8..c3b59943107 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -1985,7 +1985,23 @@ get_aggregate_argtypes(Aggref *aggref, Oid *inputTypes)
        int                     numArguments = 0;
        ListCell   *lc;
 
-       Assert(list_length(aggref->aggargtypes) <= FUNC_MAX_ARGS);
+       /*
+        * Check the number of arguments to protect fixed-size arrays in 
callers.
+        *
+        * Aggregates can have at most FUNC_MAX_ARGS-1 args (compare
+        * AggregateCreate, whose error message we want to match).  Ordinarily
+        * this would have been checked while creating the Aggref, but it's
+        * possible that we are looking at a parsetree from a stored view that 
was
+        * made by a server executable with a different value of FUNC_MAX_ARGS, 
or
+        * an executable in which parse_func.c didn't enforce the correct limit.
+        */
+       if (list_length(aggref->aggargtypes) > FUNC_MAX_ARGS - 1)
+               ereport(ERROR,
+                               (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+                                errmsg_plural("aggregates cannot have more 
than %d argument",
+                                                          "aggregates cannot 
have more than %d arguments",
+                                                          FUNC_MAX_ARGS - 1,
+                                                          FUNC_MAX_ARGS - 1)));
 
        foreach(lc, aggref->aggargtypes)
        {
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 93cfe006834..6e120bb8614 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -789,6 +789,22 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List 
*fargs,
                aggref->aggtransno = -1;
                aggref->location = location;
 
+               /*
+                * The argument-count limit for aggregates is one less than for 
other
+                * kinds of functions (cf. AggregateCreate).  Now that we know 
it's an
+                * aggregate, apply the stricter limit.  We need an explicit 
check
+                * because hypothetical-set aggregates don't have a fixed 
number of
+                * arguments, so having matched the pg_proc entry proves 
nothing.
+                */
+               if (list_length(fargs) > FUNC_MAX_ARGS - 1)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+                                        errmsg_plural("aggregates cannot have 
more than %d argument",
+                                                                  "aggregates 
cannot have more than %d arguments",
+                                                                  
FUNC_MAX_ARGS - 1,
+                                                                  
FUNC_MAX_ARGS - 1),
+                                        parser_errposition(pstate, location)));
+
                /*
                 * Reject attempt to call a parameterless aggregate without (*)
                 * syntax.  This is mere pedantry but some folks insisted ...
@@ -869,6 +885,19 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List 
*fargs,
                                                 errmsg("DISTINCT is supported 
only for single-argument window aggregates")));
                }
 
+               /*
+                * As above, enforce the correct argument-count limit if it's 
really
+                * an aggregate.
+                */
+               if (wfunc->winagg && list_length(fargs) > FUNC_MAX_ARGS - 1)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+                                        errmsg_plural("aggregates cannot have 
more than %d argument",
+                                                                  "aggregates 
cannot have more than %d arguments",
+                                                                  
FUNC_MAX_ARGS - 1,
+                                                                  
FUNC_MAX_ARGS - 1),
+                                        parser_errposition(pstate, location)));
+
                /*
                 * Reject attempt to call a parameterless aggregate without (*)
                 * syntax.  This is mere pedantry but some folks insisted ...
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 05578d8f876..878b920cd10 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -1446,7 +1446,7 @@ get_func_arg_info(HeapTuple procTup,
                        ARR_ELEMTYPE(arr) != OIDOID)
                        elog(ERROR, "proallargtypes is not a 1-D Oid array or 
it contains nulls");
                Assert(numargs >= procStruct->pronargs);
-               *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
+               *p_argtypes = palloc_array(Oid, numargs);
                memcpy(*p_argtypes, ARR_DATA_PTR(arr),
                           numargs * sizeof(Oid));
        }
@@ -1455,7 +1455,7 @@ get_func_arg_info(HeapTuple procTup,
                /* If no proallargtypes, use proargtypes */
                numargs = procStruct->proargtypes.dim1;
                Assert(numargs == procStruct->pronargs);
-               *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
+               *p_argtypes = palloc_array(Oid, numargs);
                memcpy(*p_argtypes, procStruct->proargtypes.values,
                           numargs * sizeof(Oid));
        }
@@ -1534,7 +1534,7 @@ get_func_trftypes(HeapTuple procTup,
                        ARR_HASNULL(arr) ||
                        ARR_ELEMTYPE(arr) != OIDOID)
                        elog(ERROR, "protrftypes is not a 1-D Oid array or it 
contains nulls");
-               *p_trftypes = (Oid *) palloc(nelems * sizeof(Oid));
+               *p_trftypes = palloc_array(Oid, nelems);
                memcpy(*p_trftypes, ARR_DATA_PTR(arr),
                           nelems * sizeof(Oid));
 
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 904ee126d16..8639dd0e405 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -2498,6 +2498,20 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
 
        if (procStruct->pronargs > 0)
        {
+               /*
+                * Protect against overrun of fixed-size hashkey->argtypes 
array.
+                * Ordinarily the parser would have checked this long since, 
but it's
+                * possible that we are looking at a pg_proc entry that was 
made by a
+                * server executable with a different value of FUNC_MAX_ARGS.
+                */
+               if (procStruct->pronargs > FUNC_MAX_ARGS)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+                                        errmsg_plural("cannot pass more than 
%d argument to a function",
+                                                                  "cannot pass 
more than %d arguments to a function",
+                                                                  
FUNC_MAX_ARGS,
+                                                                  
FUNC_MAX_ARGS)));
+
                /* get the argument types */
                memcpy(hashkey->argtypes, procStruct->proargtypes.values,
                           procStruct->pronargs * sizeof(Oid));
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index bff00aec350..c1e75af3bab 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -1410,6 +1410,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
        volatile MemoryContext proc_cxt = NULL;
        Tcl_DString proc_internal_def;
        Tcl_DString proc_internal_body;
+       Tcl_DString proc_internal_args;
 
        /* We'll need the pg_proc tuple in any case... */
        procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
@@ -1457,16 +1458,16 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
         ************************************************************/
        Tcl_DStringInit(&proc_internal_def);
        Tcl_DStringInit(&proc_internal_body);
+       Tcl_DStringInit(&proc_internal_args);
        PG_TRY();
        {
                bool            is_trigger = OidIsValid(tgreloid);
                char            internal_proname[128];
                HeapTuple       typeTup;
                Form_pg_type typeStruct;
-               char            proc_internal_args[33 * FUNC_MAX_ARGS];
                Datum           prosrcdatum;
                char       *proc_source;
-               char            buf[48];
+               char            buf[64];
                Tcl_Interp *interp;
                int                     i;
                int                     tcl_rc;
@@ -1576,7 +1577,6 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
                 ************************************************************/
                if (!is_trigger && !is_event_trigger)
                {
-                       proc_internal_args[0] = '\0';
                        for (i = 0; i < prodesc->nargs; i++)
                        {
                                Oid                     argtype = 
procStruct->proargtypes.values[i];
@@ -1609,8 +1609,8 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
                                }
 
                                if (i > 0)
-                                       strcat(proc_internal_args, " ");
-                               strcat(proc_internal_args, buf);
+                                       Tcl_DStringAppend(&proc_internal_args, 
" ", -1);
+                               Tcl_DStringAppend(&proc_internal_args, buf, -1);
 
                                ReleaseSysCache(typeTup);
                        }
@@ -1618,13 +1618,14 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
                else if (is_trigger)
                {
                        /* trigger procedure has fixed args */
-                       strcpy(proc_internal_args,
-                                  "TG_name TG_relid TG_table_name 
TG_table_schema TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW 
__PLTcl_Tup_OLD args");
+                       Tcl_DStringAppend(&proc_internal_args,
+                                                         "TG_name TG_relid 
TG_table_name TG_table_schema TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW 
__PLTcl_Tup_OLD args",
+                                                         -1);
                }
                else if (is_event_trigger)
                {
                        /* event trigger procedure has fixed args */
-                       strcpy(proc_internal_args, "TG_event TG_tag");
+                       Tcl_DStringAppend(&proc_internal_args, "TG_event 
TG_tag", -1);
                }
 
                /************************************************************
@@ -1637,7 +1638,8 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
                 ************************************************************/
                Tcl_DStringAppendElement(&proc_internal_def, "proc");
                Tcl_DStringAppendElement(&proc_internal_def, internal_proname);
-               Tcl_DStringAppendElement(&proc_internal_def, 
proc_internal_args);
+               Tcl_DStringAppendElement(&proc_internal_def,
+                                                                
Tcl_DStringValue(&proc_internal_args));
 
                /************************************************************
                 * prefix procedure body with
@@ -1717,6 +1719,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
                        MemoryContextDelete(proc_cxt);
                Tcl_DStringFree(&proc_internal_def);
                Tcl_DStringFree(&proc_internal_body);
+               Tcl_DStringFree(&proc_internal_args);
                PG_RE_THROW();
        }
        PG_END_TRY();
@@ -1745,6 +1748,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
 
        Tcl_DStringFree(&proc_internal_def);
        Tcl_DStringFree(&proc_internal_body);
+       Tcl_DStringFree(&proc_internal_args);
 
        ReleaseSysCache(procTup);
 


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

Reply via email to