From d368e1a2a7afad5a0fc711a2ab70a83c36fa57af Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Mon, 13 Dec 2010 18:58:31 -0500 Subject: [PATCH] Allow plugins to suppress inlining and hook function entry/exit/abort. This is intended as infrastructure to allow an eventual SE-Linux plugin to support trusted procedures. KaiGai Kohei --- src/backend/optimizer/util/clauses.c | 8 ++++++ src/backend/utils/fmgr/fmgr.c | 39 +++++++++++++++++++--------- src/include/fmgr.h | 26 +++++++++++++++++++ 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 80dfaad736..fa84164b06 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -3726,6 +3726,10 @@ inline_function(Oid funcid, Oid result_type, List *args, if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) return NULL; + /* Check whether a plugin wants to hook function entry/exit */ + if (FmgrHookIsNeeded(funcid)) + return NULL; + /* * Make a temporary memory context, so that we don't leak all the stuff * that parsing might create. @@ -4158,6 +4162,10 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) if (pg_proc_aclcheck(func_oid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) return NULL; + /* Check whether a plugin wants to hook function entry/exit */ + if (FmgrHookIsNeeded(func_oid)) + return NULL; + /* * OK, let's take a look at the function's pg_proc entry. */ diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 1c9d2c2fa7..e457af9764 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -30,6 +30,11 @@ #include "utils/lsyscache.h" #include "utils/syscache.h" +/* + * Hooks for function calls + */ +PGDLLIMPORT needs_fmgr_hook_type needs_fmgr_hook = NULL; +PGDLLIMPORT fmgr_hook_type fmgr_hook = NULL; /* * Declaration for old-style function pointer type. This is now used only @@ -216,9 +221,9 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, finfo->fn_retset = procedureStruct->proretset; /* - * If it has prosecdef set, or non-null proconfig, use - * fmgr_security_definer call handler --- unless we are being called again - * by fmgr_security_definer. + * If it has prosecdef set, non-null proconfig, or if a plugin wants to + * hook function entry/exit, use fmgr_security_definer call handler --- + * unless we are being called again by fmgr_security_definer. * * When using fmgr_security_definer, function stats tracking is always * disabled at the outer level, and instead we set the flag properly in @@ -230,7 +235,8 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, */ if (!ignore_security && (procedureStruct->prosecdef || - !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig))) + !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig) || + FmgrHookIsNeeded(functionId))) { finfo->fn_addr = fmgr_security_definer; finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ @@ -857,17 +863,18 @@ struct fmgr_security_definer_cache FmgrInfo flinfo; /* lookup info for target function */ Oid userid; /* userid to set, or InvalidOid */ ArrayType *proconfig; /* GUC values to set, or NULL */ + Datum private; /* private usage for plugin modules */ }; /* - * Function handler for security-definer/proconfig functions. We extract the - * OID of the actual function and do a fmgr lookup again. Then we fetch the - * pg_proc row and copy the owner ID and proconfig fields. (All this info - * is cached for the duration of the current query.) To execute a call, - * we temporarily replace the flinfo with the cached/looked-up one, while - * keeping the outer fcinfo (which contains all the actual arguments, etc.) - * intact. This is not re-entrant, but then the fcinfo itself can't be used - * re-entrantly anyway. + * Function handler for security-definer/proconfig/plugin-hooked functions. + * We extract the OID of the actual function and do a fmgr lookup again. + * Then we fetch the pg_proc row and copy the owner ID and proconfig fields. + * (All this info is cached for the duration of the current query.) + * To execute a call, we temporarily replace the flinfo with the cached + * and looked-up one, while keeping the outer fcinfo (which contains all + * the actual arguments, etc.) intact. This is not re-entrant, but then + * the fcinfo itself can't be used re-entrantly anyway. */ static Datum fmgr_security_definer(PG_FUNCTION_ARGS) @@ -940,6 +947,10 @@ fmgr_security_definer(PG_FUNCTION_ARGS) GUC_ACTION_SAVE); } + /* function manager hook */ + if (fmgr_hook) + (*fmgr_hook)(FHET_START, &fcache->flinfo, &fcache->private); + /* * We don't need to restore GUC or userid settings on error, because the * ensuing xact or subxact abort will do that. The PG_TRY block is only @@ -968,6 +979,8 @@ fmgr_security_definer(PG_FUNCTION_ARGS) PG_CATCH(); { fcinfo->flinfo = save_flinfo; + if (fmgr_hook) + (*fmgr_hook)(FHET_ABORT, &fcache->flinfo, &fcache->private); PG_RE_THROW(); } PG_END_TRY(); @@ -978,6 +991,8 @@ fmgr_security_definer(PG_FUNCTION_ARGS) AtEOXact_GUC(true, save_nestlevel); if (OidIsValid(fcache->userid)) SetUserIdAndSecContext(save_userid, save_sec_context); + if (fmgr_hook) + (*fmgr_hook)(FHET_END, &fcache->flinfo, &fcache->private); return result; } diff --git a/src/include/fmgr.h b/src/include/fmgr.h index ca5a5eacf7..99213bc117 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -544,6 +544,32 @@ extern void **find_rendezvous_variable(const char *varName); extern int AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext); +/* + * We allow plugin modules to hook function entry/exit. This is intended + * as support for loadable security policy modules, which may want to + * perform additional privilege checks on function entry or exit, or to do + * other internal bookkeeping. To make this possible, such modules must be + * able not only to support normal function entry and exit, but also to trap + * the case where we bail out due to an error; and they must also be able to + * prevent inlining. + */ +typedef enum FmgrHookEventType +{ + FHET_START, + FHET_END, + FHET_ABORT +} FmgrHookEventType; + +typedef bool (*needs_fmgr_hook_type)(Oid fn_oid); + +typedef void (*fmgr_hook_type)(FmgrHookEventType event, + FmgrInfo *flinfo, Datum *private); + +extern PGDLLIMPORT needs_fmgr_hook_type needs_fmgr_hook; +extern PGDLLIMPORT fmgr_hook_type fmgr_hook; + +#define FmgrHookIsNeeded(fn_oid) \ + (!needs_fmgr_hook ? false : (*needs_fmgr_hook)(fn_oid)) /* * !!! OLD INTERFACE !!!