mirror of
https://github.com/postgres/postgres.git
synced 2025-11-25 12:03:53 +03:00
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
This commit is contained in:
@@ -111,25 +111,30 @@ struct BackgroundWorkerHandle
|
||||
static BackgroundWorkerArray *BackgroundWorkerData;
|
||||
|
||||
/*
|
||||
* List of internal background workers. These are used for mapping the
|
||||
* function name to actual function when building with EXEC_BACKEND and also
|
||||
* to allow these to be loaded outside of shared_preload_libraries.
|
||||
* List of internal background worker entry points. We need this for
|
||||
* reasons explained in LookupBackgroundWorkerFunction(), below.
|
||||
*/
|
||||
typedef struct InternalBGWorkerMain
|
||||
static const struct
|
||||
{
|
||||
char *bgw_function_name;
|
||||
bgworker_main_type bgw_main;
|
||||
} InternalBGWorkerMain;
|
||||
const char *fn_name;
|
||||
bgworker_main_type fn_addr;
|
||||
} InternalBGWorkers[] =
|
||||
|
||||
static const InternalBGWorkerMain InternalBGWorkers[] = {
|
||||
{"ParallelWorkerMain", ParallelWorkerMain},
|
||||
{"ApplyLauncherMain", ApplyLauncherMain},
|
||||
{"ApplyWorkerMain", ApplyWorkerMain},
|
||||
/* Dummy entry marking end of the array. */
|
||||
{NULL, NULL}
|
||||
{
|
||||
{
|
||||
"ParallelWorkerMain", ParallelWorkerMain
|
||||
},
|
||||
{
|
||||
"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.
|
||||
@@ -812,18 +817,10 @@ StartBackgroundWorker(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* For internal workers set the entry point to known function address.
|
||||
* 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).
|
||||
* Look up the entry point function, loading its library if necessary.
|
||||
*/
|
||||
entrypt = GetInternalBgWorkerMain(worker);
|
||||
|
||||
if (entrypt == NULL)
|
||||
entrypt = (bgworker_main_type)
|
||||
load_external_function(worker->bgw_library_name,
|
||||
worker->bgw_function_name,
|
||||
true, NULL);
|
||||
entrypt = LookupBackgroundWorkerFunction(worker->bgw_library_name,
|
||||
worker->bgw_function_name);
|
||||
|
||||
/*
|
||||
* Note that in normal processes, we would call InitPostgres here. For a
|
||||
@@ -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
|
||||
* that's loaded by shared_preload_libraries; otherwise it has no effect.
|
||||
* This can only be called directly from postmaster or in the _PG_init
|
||||
* function of a module library that's loaded by shared_preload_libraries;
|
||||
* otherwise it will have no effect.
|
||||
*/
|
||||
void
|
||||
RegisterBackgroundWorker(BackgroundWorker *worker)
|
||||
@@ -858,7 +856,7 @@ RegisterBackgroundWorker(BackgroundWorker *worker)
|
||||
(errmsg("registering background worker \"%s\"", worker->bgw_name)));
|
||||
|
||||
if (!process_shared_preload_libraries_in_progress &&
|
||||
GetInternalBgWorkerMain(worker) == NULL)
|
||||
strcmp(worker->bgw_library_name, "postgres") != 0)
|
||||
{
|
||||
if (!IsUnderPostmaster)
|
||||
ereport(LOG,
|
||||
@@ -1193,26 +1191,45 @@ TerminateBackgroundWorker(BackgroundWorkerHandle *handle)
|
||||
}
|
||||
|
||||
/*
|
||||
* Search the known internal worker array and return its main function
|
||||
* pointer if found.
|
||||
* Look up (and possibly load) a bgworker entry point function.
|
||||
*
|
||||
* Returns NULL if not known internal worker.
|
||||
* 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().
|
||||
*
|
||||
* 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
|
||||
GetInternalBgWorkerMain(BackgroundWorker *worker)
|
||||
LookupBackgroundWorkerFunction(const char *libraryname, const char *funcname)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Internal workers always have to use postgres as library name. */
|
||||
if (strncmp(worker->bgw_library_name, "postgres", BGW_MAXLEN) != 0)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; InternalBGWorkers[i].bgw_function_name; i++)
|
||||
/*
|
||||
* If the function is to be loaded from postgres itself, search the
|
||||
* InternalBGWorkers array.
|
||||
*/
|
||||
if (strcmp(libraryname, "postgres") == 0)
|
||||
{
|
||||
if (strncmp(InternalBGWorkers[i].bgw_function_name,
|
||||
worker->bgw_function_name, BGW_MAXLEN) == 0)
|
||||
return InternalBGWorkers[i].bgw_main;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < lengthof(InternalBGWorkers); i++)
|
||||
{
|
||||
if (strcmp(InternalBGWorkers[i].fn_name, funcname) == 0)
|
||||
return InternalBGWorkers[i].fn_addr;
|
||||
}
|
||||
|
||||
/* We can only reach this by programming error. */
|
||||
elog(ERROR, "internal function \"%s\" not found", funcname);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
/* Otherwise load from external library. */
|
||||
return (bgworker_main_type)
|
||||
load_external_function(libraryname, funcname, true, NULL);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user