Repair insufficiently careful type checking for SQL-language functions:
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 2 Feb 2007 00:04:16 +0000 (00:04 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 2 Feb 2007 00:04:16 +0000 (00:04 +0000)
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

src/backend/catalog/pg_proc.c
src/backend/executor/functions.c
src/include/catalog/pg_proc.h

index 3163548242aa2686fe240b2b6c10bee1c36ddcf2..c96af77c46c0144bbcb188975bf19289ed019ce9 100644 (file)
@@ -33,7 +33,6 @@
 #include "utils/syscache.h"
 
 
-static void checkretval(Oid rettype, char fn_typtype, List *queryTreeList);
 Datum          fmgr_internal_validator(PG_FUNCTION_ARGS);
 Datum          fmgr_c_validator(PG_FUNCTION_ARGS);
 Datum          fmgr_sql_validator(PG_FUNCTION_ARGS);
@@ -294,15 +293,15 @@ ProcedureCreate(const char *procedureName,
 }
 
 /*
- * checkretval() -- check return value of a list of sql parse trees.
+ * check_sql_fn_retval() -- check return value of a list of sql parse trees.
  *
  * The return value of a sql function is the value returned by
  * the final query in the function.  We do some ad-hoc define-time
  * type checking here to be sure that the user is returning the
  * type he claims.
  */
-static void
-checkretval(Oid rettype, char fn_typtype, List *queryTreeList)
+void
+check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
 {
        Query      *parse;
        int                     cmd;
@@ -592,7 +591,7 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
        prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
 
        querytree_list = pg_parse_and_rewrite(prosrc, proc->proargtypes, proc->pronargs);
-       checkretval(proc->prorettype, functyptype, querytree_list);
+       check_sql_fn_retval(proc->prorettype, functyptype, querytree_list);
 
        ReleaseSysCache(tuple);
 
index b5d2050554a161bb8aa0dc914120fb4467fb72a3..822cf668feacf040cef3db58100e451ebb75035c 100644 (file)
@@ -24,6 +24,7 @@
 #include "tcop/tcopprot.h"
 #include "tcop/utility.h"
 #include "utils/builtins.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
 
@@ -53,7 +54,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 */
@@ -71,7 +72,8 @@ typedef SQLFunctionCache *SQLFunctionCachePtr;
 
 /* non-export function prototypes */
 static execution_state *init_execution_state(char *src,
-                                        Oid *argOidVect, int nargs);
+                                        Oid *argOidVect, int nargs,
+                                        Oid rettype);
 static void init_sql_fcache(FmgrInfo *finfo);
 static void postquel_start(execution_state *es);
 static TupleTableSlot *postquel_getnext(execution_state *es);
@@ -84,7 +86,8 @@ static void ShutdownSQLFunction(Datum arg);
 
 
 static execution_state *
-init_execution_state(char *src, Oid *argOidVect, int nargs)
+init_execution_state(char *src, Oid *argOidVect, int nargs,
+                                        Oid rettype)
 {
        execution_state *firstes;
        execution_state *preves;
@@ -93,6 +96,14 @@ init_execution_state(char *src, Oid *argOidVect, int nargs)
 
        queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
 
+       /*
+        * Check that the function returns the type it claims to.  Although
+        * 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.
+        */
+       check_sql_fn_retval(rettype, get_typtype(rettype), queryTree_list);
+
        firstes = NULL;
        preves = NULL;
 
@@ -150,6 +161,7 @@ static void
 init_sql_fcache(FmgrInfo *finfo)
 {
        Oid                     foid = finfo->fn_oid;
+       Oid                     rettype;
        HeapTuple       procedureTuple;
        HeapTuple       typeTuple;
        Form_pg_proc procedureStruct;
@@ -176,12 +188,14 @@ init_sql_fcache(FmgrInfo *finfo)
        /*
         * get the return type from the procedure tuple
         */
+       rettype = procedureStruct->prorettype;
+
        typeTuple = SearchSysCache(TYPEOID,
-                                                  ObjectIdGetDatum(procedureStruct->prorettype),
+                                                          ObjectIdGetDatum(rettype),
                                                           0, 0, 0);
        if (!HeapTupleIsValid(typeTuple))
                elog(ERROR, "init_sql_fcache: Cache lookup failed for type %u",
-                        procedureStruct->prorettype);
+                        rettype);
 
        typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
 
@@ -194,7 +208,7 @@ init_sql_fcache(FmgrInfo *finfo)
        fcache->typlen = typeStruct->typlen;
 
        if (typeStruct->typtype != 'c' &&
-               procedureStruct->prorettype != RECORDOID)
+               rettype != RECORDOID)
        {
                /* The return type is not a composite type, so just use byval */
                fcache->typbyval = typeStruct->typbyval;
@@ -243,7 +257,7 @@ init_sql_fcache(FmgrInfo *finfo)
                         foid);
        src = DatumGetCString(DirectFunctionCall1(textout, tmp));
 
-       fcache->func_state = init_execution_state(src, argOidVect, nargs);
+       fcache->func_state = init_execution_state(src, argOidVect, nargs, rettype);
 
        pfree(src);
 
index 091855b9981d3ca755e5ed133bc96be35282df91..62a15f5d739179088a0924f401a924b88c5b3518 100644 (file)
@@ -3150,4 +3150,7 @@ extern Oid ProcedureCreate(const char *procedureName,
                                int parameterCount,
                                const Oid *parameterTypes);
 
+extern void check_sql_fn_retval(Oid rettype, char fn_typtype,
+                                       List *queryTreeList);
+
 #endif   /* PG_PROC_H */