mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
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 <tgl@sss.pgh.pa.us> Reviewed-by: Robert Haas <robertmhaas@gmail.com> Backpatch-through: 14 Security: CVE-2026-14680
This commit is contained in:
@@ -572,6 +572,10 @@ can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids,
|
||||
if (inputTypeId == targetTypeId)
|
||||
continue;
|
||||
|
||||
/* 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;
|
||||
@@ -3168,6 +3172,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),
|
||||
|
||||
@@ -672,6 +672,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);
|
||||
|
||||
|
||||
@@ -736,6 +736,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);
|
||||
|
||||
|
||||
@@ -8044,13 +8044,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;
|
||||
|
||||
Reference in New Issue
Block a user