Commit 32470825 authored by Tom Lane's avatar Tom Lane

Avoid passing function pointers across process boundaries.

We'd already recognized that we can't pass function pointers across process
boundaries for functions in loadable modules, since a shared library could
get loaded at different addresses in different processes.  But actually the
practice doesn't work for functions in the core backend either, if we're
using EXEC_BACKEND.  This is the cause of recent failures on buildfarm
member culicidae.  Switch to passing a string function name in all cases.

Something like this needs to be back-patched into 9.6, but let's see
if the buildfarm likes it first.

Petr Jelinek, with a bunch of basically-cosmetic adjustments by me

Discussion: https://postgr.es/m/548f9c1d-eafa-e3fa-9da8-f0cc2f654e60@2ndquadrant.com
parent 5a617ab3
...@@ -198,7 +198,7 @@ pattern looks like this: ...@@ -198,7 +198,7 @@ pattern looks like this:
EnterParallelMode(); /* prohibit unsafe state changes */ EnterParallelMode(); /* prohibit unsafe state changes */
pcxt = CreateParallelContext(entrypoint, nworkers); pcxt = CreateParallelContext("library_name", "function_name", nworkers);
/* Allow space for application-specific data here. */ /* Allow space for application-specific data here. */
shm_toc_estimate_chunk(&pcxt->estimator, size); shm_toc_estimate_chunk(&pcxt->estimator, size);
......
This diff is collapsed.
...@@ -112,8 +112,7 @@ static shm_mq_handle **ExecParallelSetupTupleQueues(ParallelContext *pcxt, ...@@ -112,8 +112,7 @@ static shm_mq_handle **ExecParallelSetupTupleQueues(ParallelContext *pcxt,
static bool ExecParallelRetrieveInstrumentation(PlanState *planstate, static bool ExecParallelRetrieveInstrumentation(PlanState *planstate,
SharedExecutorInstrumentation *instrumentation); SharedExecutorInstrumentation *instrumentation);
/* Helper functions that run in the parallel worker. */ /* Helper function that runs in the parallel worker. */
static void ParallelQueryMain(dsm_segment *seg, shm_toc *toc);
static DestReceiver *ExecParallelGetReceiver(dsm_segment *seg, shm_toc *toc); static DestReceiver *ExecParallelGetReceiver(dsm_segment *seg, shm_toc *toc);
/* /*
...@@ -410,7 +409,7 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate, int nworkers) ...@@ -410,7 +409,7 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate, int nworkers)
pstmt_data = ExecSerializePlan(planstate->plan, estate); pstmt_data = ExecSerializePlan(planstate->plan, estate);
/* Create a parallel context. */ /* Create a parallel context. */
pcxt = CreateParallelContext(ParallelQueryMain, nworkers); pcxt = CreateParallelContext("postgres", "ParallelQueryMain", nworkers);
pei->pcxt = pcxt; pei->pcxt = pcxt;
/* /*
...@@ -831,7 +830,7 @@ ExecParallelInitializeWorker(PlanState *planstate, shm_toc *toc) ...@@ -831,7 +830,7 @@ ExecParallelInitializeWorker(PlanState *planstate, shm_toc *toc)
* to do this are also stored in the dsm_segment and can be accessed through * to do this are also stored in the dsm_segment and can be accessed through
* the shm_toc. * the shm_toc.
*/ */
static void void
ParallelQueryMain(dsm_segment *seg, shm_toc *toc) ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
{ {
BufferUsage *buffer_usage; BufferUsage *buffer_usage;
......
...@@ -111,25 +111,30 @@ struct BackgroundWorkerHandle ...@@ -111,25 +111,30 @@ struct BackgroundWorkerHandle
static BackgroundWorkerArray *BackgroundWorkerData; static BackgroundWorkerArray *BackgroundWorkerData;
/* /*
* List of internal background workers. These are used for mapping the * List of internal background worker entry points. We need this for
* function name to actual function when building with EXEC_BACKEND and also * reasons explained in LookupBackgroundWorkerFunction(), below.
* to allow these to be loaded outside of shared_preload_libraries.
*/ */
typedef struct InternalBGWorkerMain static const struct
{ {
char *bgw_function_name; const char *fn_name;
bgworker_main_type bgw_main; bgworker_main_type fn_addr;
} InternalBGWorkerMain; } InternalBGWorkers[] =
static const InternalBGWorkerMain InternalBGWorkers[] = { {
{"ParallelWorkerMain", ParallelWorkerMain}, {
{"ApplyLauncherMain", ApplyLauncherMain}, "ParallelWorkerMain", ParallelWorkerMain
{"ApplyWorkerMain", ApplyWorkerMain}, },
/* Dummy entry marking end of the array. */ {
{NULL, NULL} "ApplyLauncherMain", ApplyLauncherMain
},
{
"ApplyWorkerMain", ApplyWorkerMain
}
}; };
static bgworker_main_type GetInternalBgWorkerMain(BackgroundWorker *worker); /* Private functions. */
static bgworker_main_type LookupBackgroundWorkerFunction(const char *libraryname, const char *funcname);
/* /*
* Calculate shared memory needed. * Calculate shared memory needed.
...@@ -812,18 +817,10 @@ StartBackgroundWorker(void) ...@@ -812,18 +817,10 @@ StartBackgroundWorker(void)
} }
/* /*
* For internal workers set the entry point to known function address. * Look up the entry point function, loading its library if necessary.
* Otherwise use the entry point specified by library name (which will
* be loaded, if necessary) and a function name (which will be looked up
* in the named library).
*/ */
entrypt = GetInternalBgWorkerMain(worker); entrypt = LookupBackgroundWorkerFunction(worker->bgw_library_name,
worker->bgw_function_name);
if (entrypt == NULL)
entrypt = (bgworker_main_type)
load_external_function(worker->bgw_library_name,
worker->bgw_function_name,
true, NULL);
/* /*
* Note that in normal processes, we would call InitPostgres here. For a * Note that in normal processes, we would call InitPostgres here. For a
...@@ -842,10 +839,11 @@ StartBackgroundWorker(void) ...@@ -842,10 +839,11 @@ StartBackgroundWorker(void)
} }
/* /*
* Register a new background worker while processing shared_preload_libraries. * Register a new static background worker.
* *
* This can only be called in the _PG_init function of a module library * This can only be called directly from postmaster or in the _PG_init
* that's loaded by shared_preload_libraries; otherwise it has no effect. * function of a module library that's loaded by shared_preload_libraries;
* otherwise it will have no effect.
*/ */
void void
RegisterBackgroundWorker(BackgroundWorker *worker) RegisterBackgroundWorker(BackgroundWorker *worker)
...@@ -858,7 +856,7 @@ RegisterBackgroundWorker(BackgroundWorker *worker) ...@@ -858,7 +856,7 @@ RegisterBackgroundWorker(BackgroundWorker *worker)
(errmsg("registering background worker \"%s\"", worker->bgw_name))); (errmsg("registering background worker \"%s\"", worker->bgw_name)));
if (!process_shared_preload_libraries_in_progress && if (!process_shared_preload_libraries_in_progress &&
GetInternalBgWorkerMain(worker) == NULL) strcmp(worker->bgw_library_name, "postgres") != 0)
{ {
if (!IsUnderPostmaster) if (!IsUnderPostmaster)
ereport(LOG, ereport(LOG,
...@@ -1193,26 +1191,45 @@ TerminateBackgroundWorker(BackgroundWorkerHandle *handle) ...@@ -1193,26 +1191,45 @@ TerminateBackgroundWorker(BackgroundWorkerHandle *handle)
} }
/* /*
* Search the known internal worker array and return its main function * Look up (and possibly load) a bgworker entry point function.
* pointer if found. *
* For functions contained in the core code, we use library name "postgres"
* and consult the InternalBGWorkers array. External functions are
* looked up, and loaded if necessary, using load_external_function().
* *
* Returns NULL if not known internal worker. * The point of this is to pass function names as strings across process
* boundaries. We can't pass actual function addresses because of the
* possibility that the function has been loaded at a different address
* in a different process. This is obviously a hazard for functions in
* loadable libraries, but it can happen even for functions in the core code
* on platforms using EXEC_BACKEND (e.g., Windows).
*
* At some point it might be worthwhile to get rid of InternalBGWorkers[]
* in favor of applying load_external_function() for core functions too;
* but that raises portability issues that are not worth addressing now.
*/ */
static bgworker_main_type static bgworker_main_type
GetInternalBgWorkerMain(BackgroundWorker *worker) LookupBackgroundWorkerFunction(const char *libraryname, const char *funcname)
{ {
int i; /*
* If the function is to be loaded from postgres itself, search the
* InternalBGWorkers array.
*/
if (strcmp(libraryname, "postgres") == 0)
{
int i;
/* Internal workers always have to use postgres as library name. */ for (i = 0; i < lengthof(InternalBGWorkers); i++)
if (strncmp(worker->bgw_library_name, "postgres", BGW_MAXLEN) != 0) {
return NULL; if (strcmp(InternalBGWorkers[i].fn_name, funcname) == 0)
return InternalBGWorkers[i].fn_addr;
}
for (i = 0; InternalBGWorkers[i].bgw_function_name; i++) /* We can only reach this by programming error. */
{ elog(ERROR, "internal function \"%s\" not found", funcname);
if (strncmp(InternalBGWorkers[i].bgw_function_name,
worker->bgw_function_name, BGW_MAXLEN) == 0)
return InternalBGWorkers[i].bgw_main;
} }
return NULL; /* Otherwise load from external library. */
return (bgworker_main_type)
load_external_function(libraryname, funcname, true, NULL);
} }
...@@ -91,7 +91,7 @@ static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA; ...@@ -91,7 +91,7 @@ static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA;
* at less cost than repeating load_external_function. * at less cost than repeating load_external_function.
*/ */
PGFunction PGFunction
load_external_function(char *filename, char *funcname, load_external_function(const char *filename, const char *funcname,
bool signalNotFound, void **filehandle) bool signalNotFound, void **filehandle)
{ {
char *fullname; char *fullname;
...@@ -108,8 +108,12 @@ load_external_function(char *filename, char *funcname, ...@@ -108,8 +108,12 @@ load_external_function(char *filename, char *funcname,
if (filehandle) if (filehandle)
*filehandle = lib_handle; *filehandle = lib_handle;
/* Look up the function within the library */ /*
retval = (PGFunction) pg_dlsym(lib_handle, funcname); * Look up the function within the library. According to POSIX dlsym()
* should declare its second argument as "const char *", but older
* platforms might not, so for the time being we just cast away const.
*/
retval = (PGFunction) pg_dlsym(lib_handle, (char *) funcname);
if (retval == NULL && signalNotFound) if (retval == NULL && signalNotFound)
ereport(ERROR, ereport(ERROR,
...@@ -155,9 +159,10 @@ load_file(const char *filename, bool restricted) ...@@ -155,9 +159,10 @@ load_file(const char *filename, bool restricted)
* Return (PGFunction) NULL if not found. * Return (PGFunction) NULL if not found.
*/ */
PGFunction PGFunction
lookup_external_function(void *filehandle, char *funcname) lookup_external_function(void *filehandle, const char *funcname)
{ {
return (PGFunction) pg_dlsym(filehandle, funcname); /* as above, cast away const for the time being */
return (PGFunction) pg_dlsym(filehandle, (char *) funcname);
} }
......
...@@ -381,7 +381,7 @@ fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple) ...@@ -381,7 +381,7 @@ fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
* pg_proc. * pg_proc.
*/ */
const Pg_finfo_record * const Pg_finfo_record *
fetch_finfo_record(void *filehandle, char *funcname) fetch_finfo_record(void *filehandle, const char *funcname)
{ {
char *infofuncname; char *infofuncname;
PGFInfoFunction infofunc; PGFInfoFunction infofunc;
......
...@@ -35,7 +35,6 @@ typedef struct ParallelContext ...@@ -35,7 +35,6 @@ typedef struct ParallelContext
SubTransactionId subid; SubTransactionId subid;
int nworkers; int nworkers;
int nworkers_launched; int nworkers_launched;
parallel_worker_main_type entrypoint;
char *library_name; char *library_name;
char *function_name; char *function_name;
ErrorContextCallback *error_context_stack; ErrorContextCallback *error_context_stack;
...@@ -52,8 +51,7 @@ extern bool InitializingParallelWorker; ...@@ -52,8 +51,7 @@ extern bool InitializingParallelWorker;
#define IsParallelWorker() (ParallelWorkerNumber >= 0) #define IsParallelWorker() (ParallelWorkerNumber >= 0)
extern ParallelContext *CreateParallelContext(parallel_worker_main_type entrypoint, int nworkers); extern ParallelContext *CreateParallelContext(const char *library_name, const char *function_name, int nworkers);
extern ParallelContext *CreateParallelContextForExternalFunction(char *library_name, char *function_name, int nworkers);
extern void InitializeParallelDSM(ParallelContext *pcxt); extern void InitializeParallelDSM(ParallelContext *pcxt);
extern void ReinitializeParallelDSM(ParallelContext *pcxt); extern void ReinitializeParallelDSM(ParallelContext *pcxt);
extern void LaunchParallelWorkers(ParallelContext *pcxt); extern void LaunchParallelWorkers(ParallelContext *pcxt);
......
...@@ -38,4 +38,6 @@ extern void ExecParallelFinish(ParallelExecutorInfo *pei); ...@@ -38,4 +38,6 @@ extern void ExecParallelFinish(ParallelExecutorInfo *pei);
extern void ExecParallelCleanup(ParallelExecutorInfo *pei); extern void ExecParallelCleanup(ParallelExecutorInfo *pei);
extern void ExecParallelReinitialize(ParallelExecutorInfo *pei); extern void ExecParallelReinitialize(ParallelExecutorInfo *pei);
extern void ParallelQueryMain(dsm_segment *seg, shm_toc *toc);
#endif /* EXECPARALLEL_H */ #endif /* EXECPARALLEL_H */
...@@ -657,7 +657,7 @@ extern bytea *OidSendFunctionCall(Oid functionId, Datum val); ...@@ -657,7 +657,7 @@ extern bytea *OidSendFunctionCall(Oid functionId, Datum val);
/* /*
* Routines in fmgr.c * Routines in fmgr.c
*/ */
extern const Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname); extern const Pg_finfo_record *fetch_finfo_record(void *filehandle, const char *funcname);
extern void clear_external_function_hash(void *filehandle); extern void clear_external_function_hash(void *filehandle);
extern Oid fmgr_internal_function(const char *proname); extern Oid fmgr_internal_function(const char *proname);
extern Oid get_fn_expr_rettype(FmgrInfo *flinfo); extern Oid get_fn_expr_rettype(FmgrInfo *flinfo);
...@@ -673,9 +673,9 @@ extern bool CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid); ...@@ -673,9 +673,9 @@ extern bool CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid);
*/ */
extern char *Dynamic_library_path; extern char *Dynamic_library_path;
extern PGFunction load_external_function(char *filename, char *funcname, extern PGFunction load_external_function(const char *filename, const char *funcname,
bool signalNotFound, void **filehandle); bool signalNotFound, void **filehandle);
extern PGFunction lookup_external_function(void *filehandle, char *funcname); extern PGFunction lookup_external_function(void *filehandle, const char *funcname);
extern void load_file(const char *filename, bool restricted); extern void load_file(const char *filename, bool restricted);
extern void **find_rendezvous_variable(const char *varName); extern void **find_rendezvous_variable(const char *varName);
extern Size EstimateLibraryStateSpace(void); extern Size EstimateLibraryStateSpace(void);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment