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 * 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 * 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. * to be sure that the user is returning the type he claims.
* *
* This is normally applied during function definition, but in the case * For a polymorphic function the passed rettype must be the actual resolved
* of a function with polymorphic arguments, we instead apply it during * output type of the function; we should never see ANYARRAY or ANYELEMENT
* function execution startup. The rettype is then the actual resolved * as rettype. (This means we can't check the type during function definition
* output type of the function, rather than the declared type. (Therefore, * of a polymorphic function.)
* we should never see ANYARRAY or ANYELEMENT as rettype.)
*/ */
void void
check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList) check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 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 typbyval; /* true if return type is pass by value */
bool returnsTuple; /* true if return type is a tuple */ bool returnsTuple; /* true if return type is a tuple */
bool shutdown_reg; /* true if registered shutdown callback */ bool shutdown_reg; /* true if registered shutdown callback */
@ -77,7 +77,7 @@ typedef SQLFunctionCache *SQLFunctionCachePtr;
/* non-export function prototypes */ /* non-export function prototypes */
static execution_state *init_execution_state(char *src, static execution_state *init_execution_state(char *src,
Oid *argOidVect, int nargs, Oid *argOidVect, int nargs,
Oid rettype, bool haspolyarg); Oid rettype);
static void init_sql_fcache(FmgrInfo *finfo); static void init_sql_fcache(FmgrInfo *finfo);
static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache); static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
static TupleTableSlot *postquel_getnext(execution_state *es); static TupleTableSlot *postquel_getnext(execution_state *es);
@ -93,7 +93,7 @@ static void ShutdownSQLFunction(Datum arg);
static execution_state * static execution_state *
init_execution_state(char *src, Oid *argOidVect, int nargs, init_execution_state(char *src, Oid *argOidVect, int nargs,
Oid rettype, bool haspolyarg) Oid rettype)
{ {
execution_state *firstes; execution_state *firstes;
execution_state *preves; 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); queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
/* /*
* If the function has any arguments declared as polymorphic types, * Check that the function returns the type it claims to. Although
* then it wasn't type-checked at definition time; must do so now. * 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; firstes = NULL;
preves = NULL; preves = NULL;
@ -150,7 +152,6 @@ init_sql_fcache(FmgrInfo *finfo)
Form_pg_type typeStruct; Form_pg_type typeStruct;
SQLFunctionCachePtr fcache; SQLFunctionCachePtr fcache;
Oid *argOidVect; Oid *argOidVect;
bool haspolyarg;
char *src; char *src;
int nargs; int nargs;
Datum tmp; Datum tmp;
@ -226,12 +227,9 @@ init_sql_fcache(FmgrInfo *finfo)
fcache->funcSlot = NULL; fcache->funcSlot = NULL;
/* /*
* Parse and plan the queries. We need the argument type info to pass * We need the argument type info to pass to the parser.
* to the parser.
*/ */
nargs = procedureStruct->pronargs; nargs = procedureStruct->pronargs;
haspolyarg = false;
if (nargs > 0) if (nargs > 0)
{ {
int argnum; int argnum;
@ -254,13 +252,15 @@ init_sql_fcache(FmgrInfo *finfo)
errmsg("could not determine actual type of argument declared %s", errmsg("could not determine actual type of argument declared %s",
format_type_be(argOidVect[argnum])))); format_type_be(argOidVect[argnum]))));
argOidVect[argnum] = argtype; argOidVect[argnum] = argtype;
haspolyarg = true;
} }
} }
} }
else else
argOidVect = (Oid *) NULL; argOidVect = (Oid *) NULL;
/*
* Parse and rewrite the queries in the function text.
*/
tmp = SysCacheGetAttr(PROCOID, tmp = SysCacheGetAttr(PROCOID,
procedureTuple, procedureTuple,
Anum_pg_proc_prosrc, Anum_pg_proc_prosrc,
@ -269,8 +269,7 @@ init_sql_fcache(FmgrInfo *finfo)
elog(ERROR, "null prosrc for function %u", foid); elog(ERROR, "null prosrc for function %u", foid);
src = DatumGetCString(DirectFunctionCall1(textout, tmp)); src = DatumGetCString(DirectFunctionCall1(textout, tmp));
fcache->func_state = init_execution_state(src, argOidVect, nargs, fcache->func_state = init_execution_state(src, argOidVect, nargs, rettype);
rettype, haspolyarg);
pfree(src); pfree(src);

View File

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