Skip to content

Commit e14ed8e

Browse files
committed
Block signals while computing the sleep time in postmaster's main loop.
DetermineSleepTime() was previously called without blocked signals. That's not good, because it allows signal handlers to interrupt its workings. DetermineSleepTime() was added in 9.3 with the addition of background workers (da07a1e), where it only read from BackgroundWorkerList. Since 9.4, where dynamic background workers were added (7f7485a), the list is also manipulated in DetermineSleepTime(). That's bad because the list now can be persistently corrupted if modified by both a signal handler and DetermineSleepTime(). This was discovered during the investigation of hangs on buildfarm member anole. It's unclear whether this bug is the source of these hangs or not, but it's worth fixing either way. I have confirmed that it can cause crashes. It luckily looks like this only can cause problems when bgworkers are actively used. Discussion: 20140929193733.GB14400@awork2.anarazel.de Backpatch to 9.3 where background workers were introduced.
1 parent ce84b06 commit e14ed8e

File tree

1 file changed

+16
-10
lines changed

1 file changed

+16
-10
lines changed

src/backend/postmaster/postmaster.c

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,6 +1486,8 @@ DetermineSleepTime(struct timeval * timeout)
14861486

14871487
/*
14881488
* Main idle loop of postmaster
1489+
*
1490+
* NB: Needs to be called with signals blocked
14891491
*/
14901492
static int
14911493
ServerLoop(void)
@@ -1507,34 +1509,38 @@ ServerLoop(void)
15071509
/*
15081510
* Wait for a connection request to arrive.
15091511
*
1512+
* We block all signals except while sleeping. That makes it safe for
1513+
* signal handlers, which again block all signals while executing, to
1514+
* do nontrivial work.
1515+
*
15101516
* If we are in PM_WAIT_DEAD_END state, then we don't want to accept
1511-
* any new connections, so we don't call select() at all; just sleep
1512-
* for a little bit with signals unblocked.
1517+
* any new connections, so we don't call select(), and just sleep.
15131518
*/
15141519
memcpy((char *) &rmask, (char *) &readmask, sizeof(fd_set));
15151520

1516-
PG_SETMASK(&UnBlockSig);
1517-
15181521
if (pmState == PM_WAIT_DEAD_END)
15191522
{
1523+
PG_SETMASK(&UnBlockSig);
1524+
15201525
pg_usleep(100000L); /* 100 msec seems reasonable */
15211526
selres = 0;
1527+
1528+
PG_SETMASK(&BlockSig);
15221529
}
15231530
else
15241531
{
15251532
/* must set timeout each time; some OSes change it! */
15261533
struct timeval timeout;
15271534

1535+
/* Needs to run with blocked signals! */
15281536
DetermineSleepTime(&timeout);
15291537

1538+
PG_SETMASK(&UnBlockSig);
1539+
15301540
selres = select(nSockets, &rmask, NULL, NULL, &timeout);
1531-
}
15321541

1533-
/*
1534-
* Block all signals until we wait again. (This makes it safe for our
1535-
* signal handlers to do nontrivial work.)
1536-
*/
1537-
PG_SETMASK(&BlockSig);
1542+
PG_SETMASK(&BlockSig);
1543+
}
15381544

15391545
/* Now check the select() result */
15401546
if (selres < 0)

0 commit comments

Comments
 (0)