Commit 2113ac4c authored by Robert Haas's avatar Robert Haas

Don't use bgw_main even to specify in-core bgworker entrypoints.

On EXEC_BACKEND builds, this can fail if ASLR is in use.

Backpatch to 9.5.  On master, completely remove the bgw_main field
completely, since there is no situation in which it is safe for an
EXEC_BACKEND build.  On 9.6 and 9.5, leave the field intact to avoid
breaking things for third-party code that doesn't care about working
under EXEC_BACKEND.  Prior to 9.5, there are no in-core bgworker
entrypoints.

Petr Jelinek, reviewed by me.

Discussion: http://postgr.es/m/09d8ad33-4287-a09b-a77f-77f8761adb5e@2ndquadrant.com
parent c281cd5f
...@@ -54,9 +54,8 @@ typedef struct BackgroundWorker ...@@ -54,9 +54,8 @@ typedef struct BackgroundWorker
int bgw_flags; int bgw_flags;
BgWorkerStartTime bgw_start_time; BgWorkerStartTime bgw_start_time;
int bgw_restart_time; /* in seconds, or BGW_NEVER_RESTART */ int bgw_restart_time; /* in seconds, or BGW_NEVER_RESTART */
bgworker_main_type bgw_main; char bgw_library_name[BGW_MAXLEN];
char bgw_library_name[BGW_MAXLEN]; /* only if bgw_main is NULL */ char bgw_function_name[BGW_MAXLEN];
char bgw_function_name[BGW_MAXLEN]; /* only if bgw_main is NULL */
Datum bgw_main_arg; Datum bgw_main_arg;
char bgw_extra[BGW_EXTRALEN]; char bgw_extra[BGW_EXTRALEN];
int bgw_notify_pid; int bgw_notify_pid;
...@@ -130,27 +129,13 @@ typedef struct BackgroundWorker ...@@ -130,27 +129,13 @@ typedef struct BackgroundWorker
process in case of a crash. process in case of a crash.
</para> </para>
<para>
<structfield>bgw_main</structfield> is a pointer to the function to run when
the process is started. This field can only safely be used to launch
functions within the core server, because shared libraries may be loaded
at different starting addresses in different backend processes. This will
happen on all platforms when the library is loaded using any mechanism
other than <xref linkend="guc-shared-preload-libraries">. Even when that
mechanism is used, address space layout variations will still occur on
Windows, and when <literal>EXEC_BACKEND</> is used. Therefore, most users
of this API should set this field to NULL. If it is non-NULL, it takes
precedence over <structfield>bgw_library_name</> and
<structfield>bgw_function_name</>.
</para>
<para> <para>
<structfield>bgw_library_name</structfield> is the name of a library in <structfield>bgw_library_name</structfield> is the name of a library in
which the initial entry point for the background worker should be sought. which the initial entry point for the background worker should be sought.
The named library will be dynamically loaded by the worker process and The named library will be dynamically loaded by the worker process and
<structfield>bgw_function_name</structfield> will be used to identify the <structfield>bgw_function_name</structfield> will be used to identify the
function to be called. If loading a function from the core code, function to be called. If loading a function from the core code, this must
<structfield>bgw_main</> should be set instead. be set to "postgres".
</para> </para>
<para> <para>
...@@ -161,13 +146,10 @@ typedef struct BackgroundWorker ...@@ -161,13 +146,10 @@ typedef struct BackgroundWorker
<para> <para>
<structfield>bgw_main_arg</structfield> is the <type>Datum</> argument <structfield>bgw_main_arg</structfield> is the <type>Datum</> argument
to the background worker main function. Regardless of whether that to the background worker main function. This main function should take a
function is specified via <structfield>bgw_main</> or via the combination single argument of type <type>Datum</> and return <type>void</>.
of <function>bgw_library_name</> and <function>bgw_function_name</>, <structfield>bgw_main_arg</structfield> will be passed as the argument.
this main function should take a single argument of type <type>Datum</> In addition, the global variable <literal>MyBgworkerEntry</literal>
and return <type>void</>. <structfield>bgw_main_arg</structfield> will be
passed as the argument. In addition, the global variable
<literal>MyBgworkerEntry</literal>
points to a copy of the <structname>BackgroundWorker</structname> structure points to a copy of the <structname>BackgroundWorker</structname> structure
passed at registration time; the worker may find it helpful to examine passed at registration time; the worker may find it helpful to examine
this structure. this structure.
...@@ -215,7 +197,7 @@ typedef struct BackgroundWorker ...@@ -215,7 +197,7 @@ typedef struct BackgroundWorker
<para> <para>
Signals are initially blocked when control reaches the Signals are initially blocked when control reaches the
<structfield>bgw_main</> function, and must be unblocked by it; this is to background worker's main function, and must be unblocked by it; this is to
allow the process to customize its signal handlers, if necessary. allow the process to customize its signal handlers, if necessary.
Signals can be unblocked in the new process by calling Signals can be unblocked in the new process by calling
<function>BackgroundWorkerUnblockSignals</> and blocked by calling <function>BackgroundWorkerUnblockSignals</> and blocked by calling
......
...@@ -110,7 +110,6 @@ static dlist_head pcxt_list = DLIST_STATIC_INIT(pcxt_list); ...@@ -110,7 +110,6 @@ static dlist_head pcxt_list = DLIST_STATIC_INIT(pcxt_list);
/* Private functions. */ /* Private functions. */
static void HandleParallelMessage(ParallelContext *pcxt, int i, StringInfo msg); static void HandleParallelMessage(ParallelContext *pcxt, int i, StringInfo msg);
static void ParallelExtensionTrampoline(dsm_segment *seg, shm_toc *toc); static void ParallelExtensionTrampoline(dsm_segment *seg, shm_toc *toc);
static void ParallelWorkerMain(Datum main_arg);
static void WaitForParallelWorkersToExit(ParallelContext *pcxt); static void WaitForParallelWorkersToExit(ParallelContext *pcxt);
...@@ -458,7 +457,8 @@ LaunchParallelWorkers(ParallelContext *pcxt) ...@@ -458,7 +457,8 @@ LaunchParallelWorkers(ParallelContext *pcxt)
| BGWORKER_CLASS_PARALLEL; | BGWORKER_CLASS_PARALLEL;
worker.bgw_start_time = BgWorkerStart_ConsistentState; worker.bgw_start_time = BgWorkerStart_ConsistentState;
worker.bgw_restart_time = BGW_NEVER_RESTART; worker.bgw_restart_time = BGW_NEVER_RESTART;
worker.bgw_main = ParallelWorkerMain; sprintf(worker.bgw_library_name, "postgres");
sprintf(worker.bgw_function_name, "ParallelWorkerMain");
worker.bgw_main_arg = UInt32GetDatum(dsm_segment_handle(pcxt->seg)); worker.bgw_main_arg = UInt32GetDatum(dsm_segment_handle(pcxt->seg));
worker.bgw_notify_pid = MyProcPid; worker.bgw_notify_pid = MyProcPid;
memset(&worker.bgw_extra, 0, BGW_EXTRALEN); memset(&worker.bgw_extra, 0, BGW_EXTRALEN);
...@@ -931,7 +931,7 @@ AtEOXact_Parallel(bool isCommit) ...@@ -931,7 +931,7 @@ AtEOXact_Parallel(bool isCommit)
/* /*
* Main entrypoint for parallel workers. * Main entrypoint for parallel workers.
*/ */
static void void
ParallelWorkerMain(Datum main_arg) ParallelWorkerMain(Datum main_arg)
{ {
dsm_segment *seg; dsm_segment *seg;
......
...@@ -15,12 +15,14 @@ ...@@ -15,12 +15,14 @@
#include <unistd.h> #include <unistd.h>
#include "libpq/pqsignal.h" #include "libpq/pqsignal.h"
#include "access/parallel.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pgstat.h" #include "pgstat.h"
#include "port/atomics.h" #include "port/atomics.h"
#include "postmaster/bgworker_internals.h" #include "postmaster/bgworker_internals.h"
#include "postmaster/postmaster.h" #include "postmaster/postmaster.h"
#include "replication/logicallauncher.h" #include "replication/logicallauncher.h"
#include "replication/logicalworker.h"
#include "storage/dsm.h" #include "storage/dsm.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/latch.h" #include "storage/latch.h"
...@@ -109,14 +111,26 @@ struct BackgroundWorkerHandle ...@@ -109,14 +111,26 @@ struct BackgroundWorkerHandle
static BackgroundWorkerArray *BackgroundWorkerData; static BackgroundWorkerArray *BackgroundWorkerData;
/* /*
* List of workers that are allowed to be started outside of * List of internal background workers. These are used for mapping the
* shared_preload_libraries. * function name to actual function when building with EXEC_BACKEND and also
* to allow these to be loaded outside of shared_preload_libraries.
*/ */
static const bgworker_main_type InternalBGWorkers[] = { typedef struct InternalBGWorkerMain
ApplyLauncherMain, {
NULL char *bgw_function_name;
bgworker_main_type bgw_main;
} InternalBGWorkerMain;
static const InternalBGWorkerMain InternalBGWorkers[] = {
{"ParallelWorkerMain", ParallelWorkerMain},
{"ApplyLauncherMain", ApplyLauncherMain},
{"ApplyWorkerMain", ApplyWorkerMain},
/* Dummy entry marking end of the array. */
{NULL, NULL}
}; };
static bgworker_main_type GetInternalBgWorkerMain(BackgroundWorker *worker);
/* /*
* Calculate shared memory needed. * Calculate shared memory needed.
*/ */
...@@ -341,7 +355,6 @@ BackgroundWorkerStateChange(void) ...@@ -341,7 +355,6 @@ BackgroundWorkerStateChange(void)
rw->rw_worker.bgw_flags = slot->worker.bgw_flags; rw->rw_worker.bgw_flags = slot->worker.bgw_flags;
rw->rw_worker.bgw_start_time = slot->worker.bgw_start_time; rw->rw_worker.bgw_start_time = slot->worker.bgw_start_time;
rw->rw_worker.bgw_restart_time = slot->worker.bgw_restart_time; rw->rw_worker.bgw_restart_time = slot->worker.bgw_restart_time;
rw->rw_worker.bgw_main = slot->worker.bgw_main;
rw->rw_worker.bgw_main_arg = slot->worker.bgw_main_arg; rw->rw_worker.bgw_main_arg = slot->worker.bgw_main_arg;
memcpy(rw->rw_worker.bgw_extra, slot->worker.bgw_extra, BGW_EXTRALEN); memcpy(rw->rw_worker.bgw_extra, slot->worker.bgw_extra, BGW_EXTRALEN);
...@@ -763,17 +776,14 @@ StartBackgroundWorker(void) ...@@ -763,17 +776,14 @@ StartBackgroundWorker(void)
} }
/* /*
* If bgw_main is set, we use that value as the initial entrypoint. * For internal workers set the entry point to known function address.
* However, if the library containing the entrypoint wasn't loaded at * Otherwise use the entry point specified by library name (which will
* postmaster startup time, passing it as a direct function pointer is not * be loaded, if necessary) and a function name (which will be looked up
* possible. To work around that, we allow callers for whom a function * in the named library).
* pointer is not available to pass a library name (which will be loaded,
* if necessary) and a function name (which will be looked up in the named
* library).
*/ */
if (worker->bgw_main != NULL) entrypt = GetInternalBgWorkerMain(worker);
entrypt = worker->bgw_main;
else if (entrypt == NULL)
entrypt = (bgworker_main_type) entrypt = (bgworker_main_type)
load_external_function(worker->bgw_library_name, load_external_function(worker->bgw_library_name,
worker->bgw_function_name, worker->bgw_function_name,
...@@ -806,23 +816,13 @@ RegisterBackgroundWorker(BackgroundWorker *worker) ...@@ -806,23 +816,13 @@ RegisterBackgroundWorker(BackgroundWorker *worker)
{ {
RegisteredBgWorker *rw; RegisteredBgWorker *rw;
static int numworkers = 0; static int numworkers = 0;
bool internal = false;
int i;
if (!IsUnderPostmaster) if (!IsUnderPostmaster)
ereport(DEBUG1, ereport(DEBUG1,
(errmsg("registering background worker \"%s\"", worker->bgw_name))); (errmsg("registering background worker \"%s\"", worker->bgw_name)));
for (i = 0; InternalBGWorkers[i]; i++) if (!process_shared_preload_libraries_in_progress &&
{ GetInternalBgWorkerMain(worker) == NULL)
if (worker->bgw_main == InternalBGWorkers[i])
{
internal = true;
break;
}
}
if (!process_shared_preload_libraries_in_progress && !internal)
{ {
if (!IsUnderPostmaster) if (!IsUnderPostmaster)
ereport(LOG, ereport(LOG,
...@@ -1152,3 +1152,28 @@ TerminateBackgroundWorker(BackgroundWorkerHandle *handle) ...@@ -1152,3 +1152,28 @@ TerminateBackgroundWorker(BackgroundWorkerHandle *handle)
if (signal_postmaster) if (signal_postmaster)
SendPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE); SendPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE);
} }
/*
* Search the known internal worker array and return its main function
* pointer if found.
*
* Returns NULL if not known internal worker.
*/
static bgworker_main_type
GetInternalBgWorkerMain(BackgroundWorker *worker)
{
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 (strncmp(InternalBGWorkers[i].bgw_function_name,
worker->bgw_function_name, BGW_MAXLEN) == 0)
return InternalBGWorkers[i].bgw_main;
}
return NULL;
}
...@@ -295,7 +295,8 @@ logicalrep_worker_launch(Oid dbid, Oid subid, const char *subname, Oid userid, ...@@ -295,7 +295,8 @@ logicalrep_worker_launch(Oid dbid, Oid subid, const char *subname, Oid userid,
bgw.bgw_flags = BGWORKER_SHMEM_ACCESS | bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
BGWORKER_BACKEND_DATABASE_CONNECTION; BGWORKER_BACKEND_DATABASE_CONNECTION;
bgw.bgw_start_time = BgWorkerStart_RecoveryFinished; bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
bgw.bgw_main = ApplyWorkerMain; snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
snprintf(bgw.bgw_function_name, BGW_MAXLEN, "ApplyWorkerMain");
if (OidIsValid(relid)) if (OidIsValid(relid))
snprintf(bgw.bgw_name, BGW_MAXLEN, snprintf(bgw.bgw_name, BGW_MAXLEN,
"logical replication worker for subscription %u sync %u", subid, relid); "logical replication worker for subscription %u sync %u", subid, relid);
...@@ -553,7 +554,8 @@ ApplyLauncherRegister(void) ...@@ -553,7 +554,8 @@ ApplyLauncherRegister(void)
bgw.bgw_flags = BGWORKER_SHMEM_ACCESS | bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
BGWORKER_BACKEND_DATABASE_CONNECTION; BGWORKER_BACKEND_DATABASE_CONNECTION;
bgw.bgw_start_time = BgWorkerStart_RecoveryFinished; bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
bgw.bgw_main = ApplyLauncherMain; snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
snprintf(bgw.bgw_function_name, BGW_MAXLEN, "ApplyLauncherMain");
snprintf(bgw.bgw_name, BGW_MAXLEN, snprintf(bgw.bgw_name, BGW_MAXLEN,
"logical replication launcher"); "logical replication launcher");
bgw.bgw_restart_time = 5; bgw.bgw_restart_time = 5;
......
...@@ -67,4 +67,6 @@ extern void AtEOXact_Parallel(bool isCommit); ...@@ -67,4 +67,6 @@ extern void AtEOXact_Parallel(bool isCommit);
extern void AtEOSubXact_Parallel(bool isCommit, SubTransactionId mySubId); extern void AtEOSubXact_Parallel(bool isCommit, SubTransactionId mySubId);
extern void ParallelWorkerReportLastRecEnd(XLogRecPtr last_xlog_end); extern void ParallelWorkerReportLastRecEnd(XLogRecPtr last_xlog_end);
extern void ParallelWorkerMain(Datum main_arg);
#endif /* PARALLEL_H */ #endif /* PARALLEL_H */
...@@ -91,9 +91,8 @@ typedef struct BackgroundWorker ...@@ -91,9 +91,8 @@ typedef struct BackgroundWorker
int bgw_flags; int bgw_flags;
BgWorkerStartTime bgw_start_time; BgWorkerStartTime bgw_start_time;
int bgw_restart_time; /* in seconds, or BGW_NEVER_RESTART */ int bgw_restart_time; /* in seconds, or BGW_NEVER_RESTART */
bgworker_main_type bgw_main; char bgw_library_name[BGW_MAXLEN];
char bgw_library_name[BGW_MAXLEN]; /* only if bgw_main is NULL */ char bgw_function_name[BGW_MAXLEN];
char bgw_function_name[BGW_MAXLEN]; /* only if bgw_main is NULL */
Datum bgw_main_arg; Datum bgw_main_arg;
char bgw_extra[BGW_EXTRALEN]; char bgw_extra[BGW_EXTRALEN];
pid_t bgw_notify_pid; /* SIGUSR1 this backend on start/stop */ pid_t bgw_notify_pid; /* SIGUSR1 this backend on start/stop */
......
...@@ -216,7 +216,6 @@ setup_background_workers(int nworkers, dsm_segment *seg) ...@@ -216,7 +216,6 @@ setup_background_workers(int nworkers, dsm_segment *seg)
worker.bgw_flags = BGWORKER_SHMEM_ACCESS; worker.bgw_flags = BGWORKER_SHMEM_ACCESS;
worker.bgw_start_time = BgWorkerStart_ConsistentState; worker.bgw_start_time = BgWorkerStart_ConsistentState;
worker.bgw_restart_time = BGW_NEVER_RESTART; worker.bgw_restart_time = BGW_NEVER_RESTART;
worker.bgw_main = NULL; /* new worker might not have library loaded */
sprintf(worker.bgw_library_name, "test_shm_mq"); sprintf(worker.bgw_library_name, "test_shm_mq");
sprintf(worker.bgw_function_name, "test_shm_mq_main"); sprintf(worker.bgw_function_name, "test_shm_mq_main");
snprintf(worker.bgw_name, BGW_MAXLEN, "test_shm_mq"); snprintf(worker.bgw_name, BGW_MAXLEN, "test_shm_mq");
......
...@@ -347,7 +347,8 @@ _PG_init(void) ...@@ -347,7 +347,8 @@ _PG_init(void)
BGWORKER_BACKEND_DATABASE_CONNECTION; BGWORKER_BACKEND_DATABASE_CONNECTION;
worker.bgw_start_time = BgWorkerStart_RecoveryFinished; worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART; worker.bgw_restart_time = BGW_NEVER_RESTART;
worker.bgw_main = worker_spi_main; sprintf(worker.bgw_library_name, "worker_spi");
sprintf(worker.bgw_function_name, "worker_spi_main");
worker.bgw_notify_pid = 0; worker.bgw_notify_pid = 0;
/* /*
...@@ -378,7 +379,6 @@ worker_spi_launch(PG_FUNCTION_ARGS) ...@@ -378,7 +379,6 @@ worker_spi_launch(PG_FUNCTION_ARGS)
BGWORKER_BACKEND_DATABASE_CONNECTION; BGWORKER_BACKEND_DATABASE_CONNECTION;
worker.bgw_start_time = BgWorkerStart_RecoveryFinished; worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART; worker.bgw_restart_time = BGW_NEVER_RESTART;
worker.bgw_main = NULL; /* new worker might not have library loaded */
sprintf(worker.bgw_library_name, "worker_spi"); sprintf(worker.bgw_library_name, "worker_spi");
sprintf(worker.bgw_function_name, "worker_spi_main"); sprintf(worker.bgw_function_name, "worker_spi_main");
snprintf(worker.bgw_name, BGW_MAXLEN, "worker %d", i); snprintf(worker.bgw_name, BGW_MAXLEN, "worker %d", i);
......
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