]> granicus.if.org Git - postgresql/commitdiff
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 7e797fbd4b0ce9c1a79b886f8f0f60bf62f08ec1..8dde0578cb7aedc378ec9aa5b97b6a810c47b7fa 100644 (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)
index 6346d1abc7662d3715e0fe2af18c52d80cd25240..b23607fb0fc611c20776acf6d174ace01681a4ee 100644 (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);
 
index 75c1e9477926b7baeec539d9b8858e2cc7fc38f8..ca1f1623bd02332fa03f84cfd4b875017ea895fc 100644 (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