Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit dc71c64

Browse filesBrowse files
committed
Use _exit(2) for SIGQUIT during ProcessStartupPacket, too.
Bring the signal handling for startup-packet collection into line with the policy established in commits bedadc7 and 8e19a82, namely don't risk running atexit callbacks when handling SIGQUIT. Ideally, we'd not do so for SIGTERM or timeout interrupts either, but that change seems a bit too risky for the back branches. For now, just improve the comments in this area to describe the risk. Also relocate where BackendInitialize re-disables these interrupts, to minimize the code span where they're active. This doesn't buy a whole lot of safety, but it can't hurt. In passing, rename startup_die() to remove confusion about whether it is for the startup process. Like the previous commits, back-patch to all supported branches. Discussion: https://postgr.es/m/1850884.1599601164@sss.pgh.pa.us
1 parent 4f737ca commit dc71c64
Copy full SHA for dc71c64

File tree

Expand file treeCollapse file tree

1 file changed

+73
-31
lines changed
Filter options
Expand file treeCollapse file tree

1 file changed

+73
-31
lines changed

‎src/backend/postmaster/postmaster.c

Copy file name to clipboardExpand all lines: src/backend/postmaster/postmaster.c
+73-31Lines changed: 73 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,8 @@ static void SIGHUP_handler(SIGNAL_ARGS);
399399
static void pmdie(SIGNAL_ARGS);
400400
static void reaper(SIGNAL_ARGS);
401401
static void sigusr1_handler(SIGNAL_ARGS);
402-
static void startup_die(SIGNAL_ARGS);
402+
static void process_startup_packet_die(SIGNAL_ARGS);
403+
static void process_startup_packet_quickdie(SIGNAL_ARGS);
403404
static void dummy_handler(SIGNAL_ARGS);
404405
static void StartupPacketTimeoutHandler(void);
405406
static void CleanupBackend(int pid, int exitstatus);
@@ -4134,22 +4135,30 @@ BackendInitialize(Port *port)
41344135
whereToSendOutput = DestRemote; /* now safe to ereport to client */
41354136

41364137
/*
4137-
* We arrange for a simple exit(1) if we receive SIGTERM or SIGQUIT or
4138-
* timeout while trying to collect the startup packet. Otherwise the
4139-
* postmaster cannot shutdown the database FAST or IMMED cleanly if a
4140-
* buggy client fails to send the packet promptly. XXX it follows that
4141-
* the remainder of this function must tolerate losing control at any
4142-
* instant. Likewise, any pg_on_exit_callback registered before or during
4143-
* this function must be prepared to execute at any instant between here
4144-
* and the end of this function. Furthermore, affected callbacks execute
4145-
* partially or not at all when a second exit-inducing signal arrives
4146-
* after proc_exit_prepare() decrements on_proc_exit_index. (Thanks to
4147-
* that mechanic, callbacks need not anticipate more than one call.) This
4148-
* is fragile; it ought to instead follow the norm of handling interrupts
4149-
* at selected, safe opportunities.
4138+
* We arrange to do proc_exit(1) if we receive SIGTERM or timeout while
4139+
* trying to collect the startup packet; while SIGQUIT results in
4140+
* _exit(2). Otherwise the postmaster cannot shutdown the database FAST
4141+
* or IMMED cleanly if a buggy client fails to send the packet promptly.
4142+
*
4143+
* XXX this is pretty dangerous; signal handlers should not call anything
4144+
* as complex as proc_exit() directly. We minimize the hazard by not
4145+
* keeping these handlers active for longer than we must. However, it
4146+
* seems necessary to be able to escape out of DNS lookups as well as the
4147+
* startup packet reception proper, so we can't narrow the scope further
4148+
* than is done here.
4149+
*
4150+
* XXX it follows that the remainder of this function must tolerate losing
4151+
* control at any instant. Likewise, any pg_on_exit_callback registered
4152+
* before or during this function must be prepared to execute at any
4153+
* instant between here and the end of this function. Furthermore,
4154+
* affected callbacks execute partially or not at all when a second
4155+
* exit-inducing signal arrives after proc_exit_prepare() decrements
4156+
* on_proc_exit_index. (Thanks to that mechanic, callbacks need not
4157+
* anticipate more than one call.) This is fragile; it ought to instead
4158+
* follow the norm of handling interrupts at selected, safe opportunities.
41504159
*/
4151-
pqsignal(SIGTERM, startup_die);
4152-
pqsignal(SIGQUIT, startup_die);
4160+
pqsignal(SIGTERM, process_startup_packet_die);
4161+
pqsignal(SIGQUIT, process_startup_packet_quickdie);
41534162
InitializeTimeouts(); /* establishes SIGALRM handler */
41544163
PG_SETMASK(&StartupBlockSig);
41554164

@@ -4209,8 +4218,8 @@ BackendInitialize(Port *port)
42094218
port->remote_hostname = strdup(remote_host);
42104219

42114220
/*
4212-
* Ready to begin client interaction. We will give up and exit(1) after a
4213-
* time delay, so that a broken client can't hog a connection
4221+
* Ready to begin client interaction. We will give up and proc_exit(1)
4222+
* after a time delay, so that a broken client can't hog a connection
42144223
* indefinitely. PreAuthDelay and any DNS interactions above don't count
42154224
* against the time limit.
42164225
*
@@ -4232,6 +4241,12 @@ BackendInitialize(Port *port)
42324241
*/
42334242
status = ProcessStartupPacket(port, false);
42344243

4244+
/*
4245+
* Disable the timeout, and prevent SIGTERM/SIGQUIT again.
4246+
*/
4247+
disable_timeout(STARTUP_PACKET_TIMEOUT, false);
4248+
PG_SETMASK(&BlockSig);
4249+
42354250
/*
42364251
* Stop here if it was bad or a cancel packet. ProcessStartupPacket
42374252
* already did any appropriate error reporting.
@@ -4257,12 +4272,6 @@ BackendInitialize(Port *port)
42574272
else
42584273
init_ps_display(port->user_name, port->database_name, remote_ps_data,
42594274
update_process_title ? "authentication" : "");
4260-
4261-
/*
4262-
* Disable the timeout, and prevent SIGTERM/SIGQUIT again.
4263-
*/
4264-
disable_timeout(STARTUP_PACKET_TIMEOUT, false);
4265-
PG_SETMASK(&BlockSig);
42664275
}
42674276

42684277

@@ -5115,20 +5124,49 @@ sigusr1_handler(SIGNAL_ARGS)
51155124
}
51165125

51175126
/*
5118-
* SIGTERM or SIGQUIT while processing startup packet.
5127+
* SIGTERM while processing startup packet.
51195128
* Clean up and exit(1).
51205129
*
5121-
* XXX: possible future improvement: try to send a message indicating
5122-
* why we are disconnecting. Problem is to be sure we don't block while
5123-
* doing so, nor mess up SSL initialization. In practice, if the client
5124-
* has wedged here, it probably couldn't do anything with the message anyway.
5130+
* Running proc_exit() from a signal handler is pretty unsafe, since we
5131+
* can't know what code we've interrupted. But the alternative of using
5132+
* _exit(2) is also unpalatable, since it'd mean that a "fast shutdown"
5133+
* would cause a database crash cycle (forcing WAL replay at restart)
5134+
* if any sessions are in authentication. So we live with it for now.
5135+
*
5136+
* One might be tempted to try to send a message indicating why we are
5137+
* disconnecting. However, that would make this even more unsafe. Also,
5138+
* it seems undesirable to provide clues about the database's state to
5139+
* a client that has not yet completed authentication.
51255140
*/
51265141
static void
5127-
startup_die(SIGNAL_ARGS)
5142+
process_startup_packet_die(SIGNAL_ARGS)
51285143
{
51295144
proc_exit(1);
51305145
}
51315146

5147+
/*
5148+
* SIGQUIT while processing startup packet.
5149+
*
5150+
* Some backend has bought the farm,
5151+
* so we need to stop what we're doing and exit.
5152+
*/
5153+
static void
5154+
process_startup_packet_quickdie(SIGNAL_ARGS)
5155+
{
5156+
/*
5157+
* We DO NOT want to run proc_exit() or atexit() callbacks; they wouldn't
5158+
* be safe to run from a signal handler. Just nail the windows shut and
5159+
* get out of town.
5160+
*
5161+
* Note we do _exit(2) not _exit(1). This is to force the postmaster into
5162+
* a system reset cycle if someone sends a manual SIGQUIT to a random
5163+
* backend. (While it might be safe to do _exit(1), since this session
5164+
* shouldn't have touched shared memory yet, there seems little point in
5165+
* taking any risks.)
5166+
*/
5167+
_exit(2);
5168+
}
5169+
51325170
/*
51335171
* Dummy signal handler
51345172
*
@@ -5145,7 +5183,11 @@ dummy_handler(SIGNAL_ARGS)
51455183

51465184
/*
51475185
* Timeout while processing startup packet.
5148-
* As for startup_die(), we clean up and exit(1).
5186+
* As for process_startup_packet_die(), we clean up and exit(1).
5187+
*
5188+
* This is theoretically just as hazardous as in process_startup_packet_die(),
5189+
* although in practice we're almost certainly waiting for client input,
5190+
* which greatly reduces the risk.
51495191
*/
51505192
static void
51515193
StartupPacketTimeoutHandler(void)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.