Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit f8eb75b

Browse files
committed
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
1 parent dc4c26c commit f8eb75b

File tree

2 files changed

+29
-57
lines changed

2 files changed

+29
-57
lines changed

src/backend/executor/functions.c

Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.109 2007/01/05 22:19:27 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.110 2007/02/02 00:02:55 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -61,7 +61,7 @@ typedef struct
6161
{
6262
Oid *argtypes; /* resolved types of arguments */
6363
Oid rettype; /* actual return type */
64-
int typlen; /* length of the return type */
64+
int16 typlen; /* length of the return type */
6565
bool typbyval; /* true if return type is pass by value */
6666
bool returnsTuple; /* true if returning whole tuple result */
6767
bool shutdown_reg; /* true if registered shutdown callback */
@@ -151,12 +151,9 @@ init_sql_fcache(FmgrInfo *finfo)
151151
Oid foid = finfo->fn_oid;
152152
Oid rettype;
153153
HeapTuple procedureTuple;
154-
HeapTuple typeTuple;
155154
Form_pg_proc procedureStruct;
156-
Form_pg_type typeStruct;
157155
SQLFunctionCachePtr fcache;
158156
Oid *argOidVect;
159-
bool haspolyarg;
160157
char *src;
161158
int nargs;
162159
List *queryTree_list;
@@ -193,35 +190,17 @@ init_sql_fcache(FmgrInfo *finfo)
193190

194191
fcache->rettype = rettype;
195192

193+
/* Fetch the typlen and byval info for the result type */
194+
get_typlenbyval(rettype, &fcache->typlen, &fcache->typbyval);
195+
196196
/* Remember if function is STABLE/IMMUTABLE */
197197
fcache->readonly_func =
198198
(procedureStruct->provolatile != PROVOLATILE_VOLATILE);
199199

200-
/* Now look up the actual result type */
201-
typeTuple = SearchSysCache(TYPEOID,
202-
ObjectIdGetDatum(rettype),
203-
0, 0, 0);
204-
if (!HeapTupleIsValid(typeTuple))
205-
elog(ERROR, "cache lookup failed for type %u", rettype);
206-
typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
207-
208-
/*
209-
* get the type length and by-value flag from the type tuple; also do a
210-
* preliminary check for returnsTuple (this may prove inaccurate, see
211-
* below).
212-
*/
213-
fcache->typlen = typeStruct->typlen;
214-
fcache->typbyval = typeStruct->typbyval;
215-
fcache->returnsTuple = (typeStruct->typtype == 'c' ||
216-
rettype == RECORDOID);
217-
218200
/*
219-
* Parse and rewrite the queries. We need the argument type info to pass
220-
* to the parser.
201+
* We need the actual argument types to pass to the parser.
221202
*/
222203
nargs = procedureStruct->pronargs;
223-
haspolyarg = false;
224-
225204
if (nargs > 0)
226205
{
227206
int argnum;
@@ -244,14 +223,16 @@ init_sql_fcache(FmgrInfo *finfo)
244223
errmsg("could not determine actual type of argument declared %s",
245224
format_type_be(argOidVect[argnum]))));
246225
argOidVect[argnum] = argtype;
247-
haspolyarg = true;
248226
}
249227
}
250228
}
251229
else
252230
argOidVect = NULL;
253231
fcache->argtypes = argOidVect;
254232

233+
/*
234+
* Parse and rewrite the queries in the function text.
235+
*/
255236
tmp = SysCacheGetAttr(PROCOID,
256237
procedureTuple,
257238
Anum_pg_proc_prosrc,
@@ -263,32 +244,32 @@ init_sql_fcache(FmgrInfo *finfo)
263244
queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
264245

265246
/*
266-
* If the function has any arguments declared as polymorphic types, then
267-
* it wasn't type-checked at definition time; must do so now.
247+
* Check that the function returns the type it claims to. Although
248+
* in simple cases this was already done when the function was defined,
249+
* we have to recheck because database objects used in the function's
250+
* queries might have changed type. We'd have to do it anyway if the
251+
* function had any polymorphic arguments.
268252
*
269-
* Also, force a type-check if the declared return type is a rowtype; we
270-
* need to find out whether we are actually returning the whole tuple
271-
* result, or just regurgitating a rowtype expression result. In the
253+
* Note: we set fcache->returnsTuple according to whether we are
254+
* returning the whole tuple result or just a single column. In the
272255
* latter case we clear returnsTuple because we need not act different
273-
* from the scalar result case.
256+
* from the scalar result case, even if it's a rowtype column.
274257
*
275258
* In the returnsTuple case, check_sql_fn_retval will also construct a
276259
* JunkFilter we can use to coerce the returned rowtype to the desired
277260
* form.
278261
*/
279-
if (haspolyarg || fcache->returnsTuple)
280-
fcache->returnsTuple = check_sql_fn_retval(foid,
281-
rettype,
282-
queryTree_list,
283-
&fcache->junkFilter);
262+
fcache->returnsTuple = check_sql_fn_retval(foid,
263+
rettype,
264+
queryTree_list,
265+
&fcache->junkFilter);
284266

285267
/* Finally, plan the queries */
286268
fcache->func_state = init_execution_state(queryTree_list,
287269
fcache->readonly_func);
288270

289271
pfree(src);
290272

291-
ReleaseSysCache(typeTuple);
292273
ReleaseSysCache(procedureTuple);
293274

294275
finfo->fn_extra = (void *) fcache;
@@ -858,11 +839,10 @@ ShutdownSQLFunction(Datum arg)
858839
* the final query in the function. We do some ad-hoc type checking here
859840
* to be sure that the user is returning the type he claims.
860841
*
861-
* This is normally applied during function definition, but in the case
862-
* of a function with polymorphic arguments, we instead apply it during
863-
* function execution startup. The rettype is then the actual resolved
864-
* output type of the function, rather than the declared type. (Therefore,
865-
* we should never see ANYARRAY or ANYELEMENT as rettype.)
842+
* For a polymorphic function the passed rettype must be the actual resolved
843+
* output type of the function; we should never see ANYARRAY or ANYELEMENT
844+
* as rettype. (This means we can't check the type during function definition
845+
* of a polymorphic function.)
866846
*
867847
* The return value is true if the function returns the entire tuple result
868848
* of its final SELECT, and false otherwise. Note that because we allow

src/backend/optimizer/util/clauses.c

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.232 2007/02/01 19:10:26 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.233 2007/02/02 00:02:55 tgl Exp $
1212
*
1313
* HISTORY
1414
* AUTHOR DATE MAJOR EVENT
@@ -2751,7 +2751,6 @@ inline_function(Oid funcid, Oid result_type, List *args,
27512751
eval_const_expressions_context *context)
27522752
{
27532753
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
2754-
bool polymorphic = false;
27552754
Oid *argtypes;
27562755
char *src;
27572756
Datum tmp;
@@ -2814,15 +2813,10 @@ inline_function(Oid funcid, Oid result_type, List *args,
28142813
if (argtypes[i] == ANYARRAYOID ||
28152814
argtypes[i] == ANYELEMENTOID)
28162815
{
2817-
polymorphic = true;
28182816
argtypes[i] = exprType((Node *) list_nth(args, i));
28192817
}
28202818
}
28212819

2822-
if (funcform->prorettype == ANYARRAYOID ||
2823-
funcform->prorettype == ANYELEMENTOID)
2824-
polymorphic = true;
2825-
28262820
/* Fetch and parse the function body */
28272821
tmp = SysCacheGetAttr(PROCOID,
28282822
func_tuple,
@@ -2874,15 +2868,13 @@ inline_function(Oid funcid, Oid result_type, List *args,
28742868
newexpr = (Node *) ((TargetEntry *) linitial(querytree->targetList))->expr;
28752869

28762870
/*
2877-
* If the function has any arguments declared as polymorphic types, then
2878-
* it wasn't type-checked at definition time; must do so now. (This will
2871+
* Make sure the function (still) returns what it's declared to. This will
28792872
* raise an error if wrong, but that's okay since the function would fail
28802873
* at runtime anyway. Note we do not try this until we have verified that
28812874
* no rewriting was needed; that's probably not important, but let's be
2882-
* careful.)
2875+
* careful.
28832876
*/
2884-
if (polymorphic)
2885-
(void) check_sql_fn_retval(funcid, result_type, querytree_list, NULL);
2877+
(void) check_sql_fn_retval(funcid, result_type, querytree_list, NULL);
28862878

28872879
/*
28882880
* Additional validity checks on the expression. It mustn't return a set,

0 commit comments

Comments
 (0)