1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-18 17:41:14 +03:00

Repair insufficiently careful type checking for SQL-language functions:

we should check that the function code returns the claimed result datatype
every time we parse the function for execution.  Formerly, for simple
scalar result types we assumed the creation-time check was sufficient, but
this fails if the function selects from a table that's been redefined since
then, and even more obviously fails if check_function_bodies had been OFF.

This is a significant security hole: not only can one trivially crash the
backend, but with appropriate misuse of pass-by-reference datatypes it is
possible to read out arbitrary locations in the server process's memory,
which could allow retrieving database content the user should not be able
to see.  Our thanks to Jeff Trout for the initial report.

Security: CVE-2007-0555
This commit is contained in:
Tom Lane 2007-02-02 00:04:02 +00:00
parent 65ada7c810
commit b4ddb79af5
3 changed files with 30 additions and 38 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.109.2.1 2005/05/03 16:51:45 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.109.2.2 2007/02/02 00:04:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -350,11 +350,10 @@ ProcedureCreate(const char *procedureName,
* the final query in the function. We do some ad-hoc type checking here
* to be sure that the user is returning the type he claims.
*
* This is normally applied during function definition, but in the case
* of a function with polymorphic arguments, we instead apply it during
* function execution startup. The rettype is then the actual resolved
* output type of the function, rather than the declared type. (Therefore,
* we should never see ANYARRAY or ANYELEMENT as rettype.)
* For a polymorphic function the passed rettype must be the actual resolved
* output type of the function; we should never see ANYARRAY or ANYELEMENT
* as rettype. (This means we can't check the type during function definition
* of a polymorphic function.)
*/
void
check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.75.2.1 2004/09/06 18:23:09 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.75.2.2 2007/02/02 00:04:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -56,7 +56,7 @@ typedef struct local_es
*/
typedef struct
{
int typlen; /* length of the return type */
int16 typlen; /* length of the return type */
bool typbyval; /* true if return type is pass by value */
bool returnsTuple; /* true if return type is a tuple */
bool shutdown_reg; /* true if registered shutdown callback */
@ -77,7 +77,7 @@ typedef SQLFunctionCache *SQLFunctionCachePtr;
/* non-export function prototypes */
static execution_state *init_execution_state(char *src,
Oid *argOidVect, int nargs,
Oid rettype, bool haspolyarg);
Oid rettype);
static void init_sql_fcache(FmgrInfo *finfo);
static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
static TupleTableSlot *postquel_getnext(execution_state *es);
@ -93,7 +93,7 @@ static void ShutdownSQLFunction(Datum arg);
static execution_state *
init_execution_state(char *src, Oid *argOidVect, int nargs,
Oid rettype, bool haspolyarg)
Oid rettype)
{
execution_state *firstes;
execution_state *preves;
@ -103,11 +103,13 @@ init_execution_state(char *src, Oid *argOidVect, int nargs,
queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
/*
* If the function has any arguments declared as polymorphic types,
* then it wasn't type-checked at definition time; must do so now.
* Check that the function returns the type it claims to. Although
* in simple cases this was already done when the function was defined,
* we have to recheck because database objects used in the function's
* queries might have changed type. We'd have to do it anyway if the
* function had any polymorphic arguments.
*/
if (haspolyarg)
check_sql_fn_retval(rettype, get_typtype(rettype), queryTree_list);
check_sql_fn_retval(rettype, get_typtype(rettype), queryTree_list);
firstes = NULL;
preves = NULL;
@ -150,7 +152,6 @@ init_sql_fcache(FmgrInfo *finfo)
Form_pg_type typeStruct;
SQLFunctionCachePtr fcache;
Oid *argOidVect;
bool haspolyarg;
char *src;
int nargs;
Datum tmp;
@ -226,12 +227,9 @@ init_sql_fcache(FmgrInfo *finfo)
fcache->funcSlot = NULL;
/*
* Parse and plan the queries. We need the argument type info to pass
* to the parser.
* We need the argument type info to pass to the parser.
*/
nargs = procedureStruct->pronargs;
haspolyarg = false;
if (nargs > 0)
{
int argnum;
@ -254,13 +252,15 @@ init_sql_fcache(FmgrInfo *finfo)
errmsg("could not determine actual type of argument declared %s",
format_type_be(argOidVect[argnum]))));
argOidVect[argnum] = argtype;
haspolyarg = true;
}
}
}
else
argOidVect = (Oid *) NULL;
/*
* Parse and rewrite the queries in the function text.
*/
tmp = SysCacheGetAttr(PROCOID,
procedureTuple,
Anum_pg_proc_prosrc,
@ -269,8 +269,7 @@ init_sql_fcache(FmgrInfo *finfo)
elog(ERROR, "null prosrc for function %u", foid);
src = DatumGetCString(DirectFunctionCall1(textout, tmp));
fcache->func_state = init_execution_state(src, argOidVect, nargs,
rettype, haspolyarg);
fcache->func_state = init_execution_state(src, argOidVect, nargs, rettype);
pfree(src);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.154.2.4 2005/04/14 21:44:35 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.154.2.5 2007/02/02 00:04:02 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -1758,7 +1758,6 @@ inline_function(Oid funcid, Oid result_type, List *args,
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
char result_typtype;
bool polymorphic = false;
Oid argtypes[FUNC_MAX_ARGS];
char *src;
Datum tmp;
@ -1792,10 +1791,8 @@ inline_function(Oid funcid, Oid result_type, List *args,
if (result_typtype != 'b' &&
result_typtype != 'd')
{
if (funcform->prorettype == ANYARRAYOID ||
funcform->prorettype == ANYELEMENTOID)
polymorphic = true;
else
if (funcform->prorettype != ANYARRAYOID &&
funcform->prorettype != ANYELEMENTOID)
return NULL;
}
@ -1814,7 +1811,6 @@ inline_function(Oid funcid, Oid result_type, List *args,
if (argtypes[i] == ANYARRAYOID ||
argtypes[i] == ANYELEMENTOID)
{
polymorphic = true;
argtypes[i] = exprType((Node *) nth(i, args));
}
}
@ -1891,16 +1887,14 @@ inline_function(Oid funcid, Oid result_type, List *args,
newexpr = (Node *) ((TargetEntry *) lfirst(querytree->targetList))->expr;
/*
* If the function has any arguments declared as polymorphic types,
* then it wasn't type-checked at definition time; must do so now.
* (This will raise an error if wrong, but that's okay since the
* function would fail at runtime anyway. Note we do not try this
* until we have verified that no rewriting was needed; that's
* probably not important, but let's be careful.)
* Make sure the function (still) returns what it's declared to. This will
* raise an error if wrong, but that's okay since the function would fail
* at runtime anyway. Note we do not try this until we have verified that
* no rewriting was needed; that's probably not important, but let's be
* careful.
*/
if (polymorphic)
check_sql_fn_retval(result_type, get_typtype(result_type),
querytree_list);
check_sql_fn_retval(result_type, get_typtype(result_type),
querytree_list);
/*
* Additional validity checks on the expression. It mustn't return a