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

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

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

    Reject calls from SQL to functions that take or return type internal.
    
    Allowing that is a security hole, since there are many different
    functions with different ideas of what their "internal" argument or
    result is.  We already had a defense against the easy case of
    "'foo'::internal", but that turns out to be insufficient.  Lock down
    both function and operator syntax.  Also disallow attempts to cast to
    or from type internal; those would mostly fail anyway, but we have
    created some holes with features such as CoerceViaIO.
    
    Reported-by: Amy Burnett (OpenAI Codex Security)
    Author: Tom Lane <[email protected]>
    Reviewed-by: Robert Haas <[email protected]>
    Backpatch-through: 14
    Security: CVE-2026-14680
---
 src/backend/parser/parse_coerce.c |  8 ++++++++
 src/backend/parser/parse_func.c   | 26 ++++++++++++++++++++++++++
 src/backend/parser/parse_oper.c   | 22 ++++++++++++++++++++++
 src/pl/plpgsql/src/pl_exec.c      | 12 ++++++++++--
 4 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/src/backend/parser/parse_coerce.c 
b/src/backend/parser/parse_coerce.c
index 4af519f5a9a..cf59ef1f5c3 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -615,6 +615,10 @@ can_coerce_type(int nargs, const Oid *input_typeids, const 
Oid *target_typeids,
                if (targetTypeId == ANYTABLEOID || inputTypeId == ANYTABLEOID)
                        return false;
 
+               /* reject all cases of casting something else to/from 
"internal" */
+               if (inputTypeId == INTERNALOID || targetTypeId == INTERNALOID)
+                       return false;
+
                /* accept if target is ANY */
                if (targetTypeId == ANYOID)
                        continue;
@@ -3215,6 +3219,10 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
        if (sourceTypeId == targetTypeId)
                return COERCION_PATH_RELABELTYPE;
 
+       /* Reject all cases of casting something else to/from "internal" */
+       if (sourceTypeId == INTERNALOID || targetTypeId == INTERNALOID)
+               return COERCION_PATH_NONE;
+
        /* Look in pg_cast */
        tuple = SearchSysCache2(CASTSOURCETARGET,
                                                        
ObjectIdGetDatum(sourceTypeId),
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 6e120bb8614..3054b2f56d9 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -680,6 +680,32 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List 
*fargs,
                                                                                
           rettype,
                                                                                
           false);
 
+       /*
+        * Reject any attempt to call a function that takes or returns type
+        * internal from SQL.  (The FUNCDETAIL_COERCION case does not reach this
+        * check because of the early return above, but that's okay because we
+        * disallow coercions to or from type internal.)  Note that we are
+        * checking the resolved argument and result types, so this will reject
+        * calls to polymorphic functions that pass internal-type arguments.  
The
+        * casting rules should prevent that anyway, since we won't cast 
internal
+        * to any polymorphic type, but no harm in being doubly sure.
+        */
+       for (int i = 0; i < nargsplusdefs; i++)
+       {
+               if (declared_arg_types[i] == INTERNALOID)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("functions accepting type 
\"%s\" cannot be called explicitly",
+                                                       "internal"),
+                                        parser_errposition(pstate, location)));
+       }
+       if (rettype == INTERNALOID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("functions returning type \"%s\" cannot 
be called explicitly",
+                                               "internal"),
+                                parser_errposition(pstate, location)));
+
        /* perform the necessary typecasting of arguments */
        make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types);
 
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index bdc8f8e26af..bac0fe4ad09 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -749,6 +749,28 @@ make_op(ParseState *pstate, List *opname, Node *ltree, 
Node *rtree,
                                                                                
           opform->oprresult,
                                                                                
           false);
 
+       /*
+        * Reject any attempt to call a function that takes or returns type
+        * internal from SQL.  This is just like the check in ParseFuncOrColumn,
+        * but for operator syntax.  (Despite that, we say "function" in the 
error
+        * messages; doesn't seem worth having two sets of translatable 
strings.)
+        */
+       for (int i = 0; i < nargs; i++)
+       {
+               if (declared_arg_types[i] == INTERNALOID)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("functions accepting type 
\"%s\" cannot be called explicitly",
+                                                       "internal"),
+                                        parser_errposition(pstate, location)));
+       }
+       if (rettype == INTERNALOID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("functions returning type \"%s\" cannot 
be called explicitly",
+                                               "internal"),
+                                parser_errposition(pstate, location)));
+
        /* perform the necessary typecasting of arguments */
        make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
 
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 78a57328852..f14d5ce2ccf 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7896,13 +7896,21 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
                 * If there's no cast path according to the parser, fall back 
to using
                 * an I/O coercion; this is semantically dubious but matches 
plpgsql's
                 * historical behavior.  We would need something of the sort for
-                * UNKNOWN literals in any case.  (This is probably now only 
reachable
-                * in the case where srctype is UNKNOWN/RECORD.)
+                * UNKNOWN literals in any case.  The only case we reject is 
casting
+                * to/from INTERNAL.  (Other than that case, this is probably 
now only
+                * reachable in the case where srctype is UNKNOWN/RECORD.)
                 */
                if (cast_expr == NULL)
                {
                        CoerceViaIO *iocoerce = makeNode(CoerceViaIO);
 
+                       if (srctype == INTERNALOID || dsttype == INTERNALOID)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_CANNOT_COERCE),
+                                                errmsg("cannot cast type %s to 
%s",
+                                                               
format_type_be(srctype),
+                                                               
format_type_be(dsttype))));
+
                        iocoerce->arg = (Expr *) placeholder;
                        iocoerce->resulttype = dsttype;
                        iocoerce->resultcollid = InvalidOid;


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

Reply via email to