make-pmsignal-more-robust.patch
text/x-diff
Filename: make-pmsignal-more-robust.patch
Type: text/x-diff
Part: 0
Message:
Non-robustness in pmsignal.c
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/postmaster/postmaster.c | 33 | 13 |
| src/backend/storage/ipc/pmsignal.c | 26 | 13 |
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 383bc4776e..4a50b24b57 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -4173,7 +4173,17 @@ BackendStartup(Port *port)
* Unless it's a dead_end child, assign it a child slot number
*/
if (!bn->dead_end)
+ {
bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
+ if (bn->child_slot == 0)
+ {
+ /* No child slots ... treat like fork failure */
+ free(bn);
+ /* error is already logged, but send something to the client */
+ report_fork_failure_to_client(port, ENOMEM);
+ return STATUS_ERROR;
+ }
+ }
else
bn->child_slot = 0;
@@ -5509,23 +5519,27 @@ StartAutovacuumWorker(void)
bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
bn->bgworker_notify = false;
- bn->pid = StartAutoVacWorker();
- if (bn->pid > 0)
+ /* if we fail to get a child slot, treat it like fork failure */
+ if (bn->child_slot > 0)
{
- bn->bkend_type = BACKEND_TYPE_AUTOVAC;
- dlist_push_head(&BackendList, &bn->elem);
+ bn->pid = StartAutoVacWorker();
+ if (bn->pid > 0)
+ {
+ bn->bkend_type = BACKEND_TYPE_AUTOVAC;
+ dlist_push_head(&BackendList, &bn->elem);
#ifdef EXEC_BACKEND
- ShmemBackendArrayAdd(bn);
+ ShmemBackendArrayAdd(bn);
#endif
- /* all OK */
- return;
- }
+ /* all OK */
+ return;
+ }
- /*
- * fork failed, fall through to report -- actual error message was
- * logged by StartAutoVacWorker
- */
- (void) ReleasePostmasterChildSlot(bn->child_slot);
+ /*
+ * fork failed, fall through to report -- actual error message
+ * was logged by StartAutoVacWorker
+ */
+ (void) ReleasePostmasterChildSlot(bn->child_slot);
+ }
free(bn);
}
else
@@ -5908,6 +5922,12 @@ assign_backendlist_entry(RegisteredBgWorker *rw)
bn->cancel_key = MyCancelKey;
bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
+ if (bn->child_slot == 0)
+ {
+ /* couldn't get a slot; failure is already logged */
+ free(bn);
+ return false;
+ }
bn->bkend_type = BACKEND_TYPE_BGWORKER;
bn->dead_end = false;
bn->bgworker_notify = false;
diff --git a/src/backend/storage/ipc/pmsignal.c b/src/backend/storage/ipc/pmsignal.c
index 3f0ec5e6b8..51776779f4 100644
--- a/src/backend/storage/ipc/pmsignal.c
+++ b/src/backend/storage/ipc/pmsignal.c
@@ -75,12 +75,21 @@ struct PMSignalData
QuitSignalReason sigquit_reason; /* why SIGQUIT was sent */
/* per-child-process flags */
int num_child_flags; /* # of entries in PMChildFlags[] */
- int next_child_flag; /* next slot to try to assign */
sig_atomic_t PMChildFlags[FLEXIBLE_ARRAY_MEMBER];
};
+/* PMSignalState pointer is valid in both postmaster and child processes */
NON_EXEC_STATIC volatile PMSignalData *PMSignalState = NULL;
+/*
+ * These static variables are valid only in the postmaster. The postmaster
+ * should always use num_child_flags, not the copy in shared memory, so
+ * that it will not go insane if some failing child clobbers the copy
+ * in shared memory.
+ */
+static int num_child_flags; /* # of entries in PMChildFlags[] */
+static int next_child_flag; /* next slot to try to assign */
+
/*
* Signal handler to be notified if postmaster dies.
*/
@@ -142,7 +151,8 @@ PMSignalShmemInit(void)
{
/* initialize all flags to zeroes */
MemSet(unvolatize(PMSignalData *, PMSignalState), 0, PMSignalShmemSize());
- PMSignalState->num_child_flags = MaxLivePostmasterChildren();
+ num_child_flags = MaxLivePostmasterChildren();
+ PMSignalState->num_child_flags = num_child_flags;
}
}
@@ -210,7 +220,7 @@ GetQuitSignalReason(void)
/*
* AssignPostmasterChildSlot - select an unused slot for a new postmaster
* child process, and set its state to ASSIGNED. Returns a slot number
- * (one to N).
+ * (one to N). On failure, log a message and return zero.
*
* Only the postmaster is allowed to execute this routine, so we need no
* special locking.
@@ -218,28 +228,31 @@ GetQuitSignalReason(void)
int
AssignPostmasterChildSlot(void)
{
- int slot = PMSignalState->next_child_flag;
+ int slot = next_child_flag;
int n;
/*
* Scan for a free slot. We track the last slot assigned so as not to
* waste time repeatedly rescanning low-numbered slots.
*/
- for (n = PMSignalState->num_child_flags; n > 0; n--)
+ for (n = num_child_flags; n > 0; n--)
{
if (--slot < 0)
- slot = PMSignalState->num_child_flags - 1;
+ slot = num_child_flags - 1;
if (PMSignalState->PMChildFlags[slot] == PM_CHILD_UNUSED)
{
PMSignalState->PMChildFlags[slot] = PM_CHILD_ASSIGNED;
- PMSignalState->next_child_flag = slot;
+ next_child_flag = slot;
return slot + 1;
}
}
- /* Out of slots ... should never happen, else postmaster.c messed up */
- elog(FATAL, "no free slots in PMChildFlags array");
- return 0; /* keep compiler quiet */
+ /*
+ * Out of slots. Should never happen if postmaster.c counts children
+ * accurately, but conceivably a failing child has clobbered the array.
+ */
+ elog(LOG, "no free slots in PMChildFlags array");
+ return 0;
}
/*
@@ -254,7 +267,7 @@ ReleasePostmasterChildSlot(int slot)
{
bool result;
- Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
+ Assert(slot > 0 && slot <= num_child_flags);
slot--;
/*
@@ -269,12 +282,12 @@ ReleasePostmasterChildSlot(int slot)
/*
* IsPostmasterChildWalSender - check if given slot is in use by a
- * walsender process.
+ * walsender process. This is called only by the postmaster.
*/
bool
IsPostmasterChildWalSender(int slot)
{
- Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
+ Assert(slot > 0 && slot <= num_child_flags);
slot--;
if (PMSignalState->PMChildFlags[slot] == PM_CHILD_WALSENDER)