1
0
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:
Tom Lane
2017-04-14 23:50:16 -04:00
parent 5a617ab3e6
commit 32470825d3
9 changed files with 187 additions and 149 deletions

View File

@@ -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);
}