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

index cba798d289ce4fff60b484e05bd2393cdf98bed3..669e7cb790cf428f5f214d431139973ae27682b5 100644 (file)
@@ -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)
index 9fd3d04b3a929f4ab4192938fefc7e7aa8d5c9ca..94b56e02c1c251dee579f8b9d9dc2f95b6ec4eb6 100644 (file)
@@ -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);
 
index 1cf0b6b89c2e7a1b4a0733f5bea2f13fe10fd4a5..40677a2e72d5afb93fe020f3500eabfa872bc25d 100644 (file)
@@ -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