]> granicus.if.org Git - postgresql/commitdiff
Don't run atexit callbacks in quickdie signal handlers.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Wed, 8 Aug 2018 16:08:10 +0000 (19:08 +0300)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Wed, 8 Aug 2018 16:09:35 +0000 (19:09 +0300)
exit() is not async-signal safe. Even if the libc implementation is, 3rd
party libraries might have installed unsafe atexit() callbacks. After
receiving SIGQUIT, we really just want to exit as quickly as possible, so
we don't really want to run the atexit() callbacks anyway.

The original report by Jimmy Yih was a self-deadlock in startup_die().
However, this patch doesn't address that scenario; the signal handling
while waiting for the startup packet is more complicated. But at least this
alleviates similar problems in the SIGQUIT handlers, like that reported
by Asim R P later in the same thread.

Backpatch to 9.3 (all supported versions).

Discussion: https://www.postgresql.org/message-id/CAOMx_OAuRUHiAuCg2YgicZLzPVv5d9_H4KrL_OFsFP%3DVPekigA%40mail.gmail.com

src/backend/postmaster/bgworker.c
src/backend/postmaster/bgwriter.c
src/backend/postmaster/checkpointer.c
src/backend/postmaster/startup.c
src/backend/postmaster/walwriter.c
src/backend/replication/walreceiver.c
src/backend/tcop/postgres.c

index 0c5db3d0cee0cc90aa04877d41cfd53d76c71e46..ace3f5439832dc41e4453cbac5ef216f1f13268e 100644 (file)
@@ -535,28 +535,21 @@ SanityCheckBackgroundWorker(BackgroundWorker *worker, int elevel)
 static void
 bgworker_quickdie(SIGNAL_ARGS)
 {
-       sigaddset(&BlockSig, SIGQUIT);          /* prevent nested calls */
-       PG_SETMASK(&BlockSig);
-
-       /*
-        * We DO NOT want to run proc_exit() callbacks -- we're here because
-        * shared memory may be corrupted, so we don't want to try to clean up our
-        * transaction.  Just nail the windows shut and get out of town.  Now that
-        * there's an atexit callback to prevent third-party code from breaking
-        * things by calling exit() directly, we have to reset the callbacks
-        * explicitly to make this work as intended.
-        */
-       on_exit_reset();
-
        /*
-        * Note we do exit(2) not exit(0).  This is to force the postmaster into a
-        * system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
+        * We DO NOT want to run proc_exit() or atexit() callbacks -- we're here
+        * because shared memory may be corrupted, so we don't want to try to
+        * clean up our transaction.  Just nail the windows shut and get out of
+        * town.  The callbacks wouldn't be safe to run from a signal handler,
+        * anyway.
+        *
+        * Note we do _exit(2) not _exit(0).  This is to force the postmaster into
+        * a system reset cycle if someone sends a manual SIGQUIT to a random
         * backend.  This is necessary precisely because we don't clean up our
         * shared memory state.  (The "dead man switch" mechanism in pmsignal.c
         * should ensure the postmaster sees this as a crash, too, but no harm in
         * being doubly sure.)
         */
-       exit(2);
+       _exit(2);
 }
 
 /*
index 65465d6a47722712d01d9af96d9a1435c2cd4eca..16a70fe92ff42ec98263e1dacd4194e06ee40776 100644 (file)
@@ -396,27 +396,21 @@ BackgroundWriterMain(void)
 static void
 bg_quickdie(SIGNAL_ARGS)
 {
-       PG_SETMASK(&BlockSig);
-
        /*
-        * We DO NOT want to run proc_exit() callbacks -- we're here because
-        * shared memory may be corrupted, so we don't want to try to clean up our
-        * transaction.  Just nail the windows shut and get out of town.  Now that
-        * there's an atexit callback to prevent third-party code from breaking
-        * things by calling exit() directly, we have to reset the callbacks
-        * explicitly to make this work as intended.
-        */
-       on_exit_reset();
-
-       /*
-        * Note we do exit(2) not exit(0).  This is to force the postmaster into a
-        * system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
+        * We DO NOT want to run proc_exit() or atexit() callbacks -- we're here
+        * because shared memory may be corrupted, so we don't want to try to
+        * clean up our transaction.  Just nail the windows shut and get out of
+        * town.  The callbacks wouldn't be safe to run from a signal handler,
+        * anyway.
+        *
+        * Note we do _exit(2) not _exit(0).  This is to force the postmaster into
+        * a system reset cycle if someone sends a manual SIGQUIT to a random
         * backend.  This is necessary precisely because we don't clean up our
         * shared memory state.  (The "dead man switch" mechanism in pmsignal.c
         * should ensure the postmaster sees this as a crash, too, but no harm in
         * being doubly sure.)
         */
-       exit(2);
+       _exit(2);
 }
 
 /* SIGHUP: set flag to re-read config file at next convenient time */
index 3b3a09ef8860b0497a415cec0ca1d823aaa78841..0a8b6b6c9ed71ae9a0958580ad884bf93cd0f125 100644 (file)
@@ -814,27 +814,21 @@ IsCheckpointOnSchedule(double progress)
 static void
 chkpt_quickdie(SIGNAL_ARGS)
 {
-       PG_SETMASK(&BlockSig);
-
        /*
-        * We DO NOT want to run proc_exit() callbacks -- we're here because
-        * shared memory may be corrupted, so we don't want to try to clean up our
-        * transaction.  Just nail the windows shut and get out of town.  Now that
-        * there's an atexit callback to prevent third-party code from breaking
-        * things by calling exit() directly, we have to reset the callbacks
-        * explicitly to make this work as intended.
-        */
-       on_exit_reset();
-
-       /*
-        * Note we do exit(2) not exit(0).  This is to force the postmaster into a
-        * system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
+        * We DO NOT want to run proc_exit() or atexit() callbacks -- we're here
+        * because shared memory may be corrupted, so we don't want to try to
+        * clean up our transaction.  Just nail the windows shut and get out of
+        * town.  The callbacks wouldn't be safe to run from a signal handler,
+        * anyway.
+        *
+        * Note we do _exit(2) not _exit(0).  This is to force the postmaster into
+        * a system reset cycle if someone sends a manual SIGQUIT to a random
         * backend.  This is necessary precisely because we don't clean up our
         * shared memory state.  (The "dead man switch" mechanism in pmsignal.c
         * should ensure the postmaster sees this as a crash, too, but no harm in
         * being doubly sure.)
         */
-       exit(2);
+       _exit(2);
 }
 
 /* SIGHUP: set flag to re-read config file at next convenient time */
index 581837a156740fb11e6902a010e23ccdfcf035cc..51054bf995694a467ac15bdb8463837d3907d890 100644 (file)
@@ -68,27 +68,21 @@ static void StartupProcSigHupHandler(SIGNAL_ARGS);
 static void
 startupproc_quickdie(SIGNAL_ARGS)
 {
-       PG_SETMASK(&BlockSig);
-
-       /*
-        * We DO NOT want to run proc_exit() callbacks -- we're here because
-        * shared memory may be corrupted, so we don't want to try to clean up our
-        * transaction.  Just nail the windows shut and get out of town.  Now that
-        * there's an atexit callback to prevent third-party code from breaking
-        * things by calling exit() directly, we have to reset the callbacks
-        * explicitly to make this work as intended.
-        */
-       on_exit_reset();
-
        /*
-        * Note we do exit(2) not exit(0).  This is to force the postmaster into a
-        * system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
+        * We DO NOT want to run proc_exit() or atexit() callbacks -- we're here
+        * because shared memory may be corrupted, so we don't want to try to
+        * clean up our transaction.  Just nail the windows shut and get out of
+        * town.  The callbacks wouldn't be safe to run from a signal handler,
+        * anyway.
+        *
+        * Note we do _exit(2) not _exit(0).  This is to force the postmaster into
+        * a system reset cycle if someone sends a manual SIGQUIT to a random
         * backend.  This is necessary precisely because we don't clean up our
         * shared memory state.  (The "dead man switch" mechanism in pmsignal.c
         * should ensure the postmaster sees this as a crash, too, but no harm in
         * being doubly sure.)
         */
-       exit(2);
+       _exit(2);
 }
 
 
index a44b5a80eb4e0ddff0ecd6ab90093003073de7a1..ba99c33dbc8bb21d984a97220735b070e50fb1df 100644 (file)
@@ -315,27 +315,21 @@ WalWriterMain(void)
 static void
 wal_quickdie(SIGNAL_ARGS)
 {
-       PG_SETMASK(&BlockSig);
-
        /*
-        * We DO NOT want to run proc_exit() callbacks -- we're here because
-        * shared memory may be corrupted, so we don't want to try to clean up our
-        * transaction.  Just nail the windows shut and get out of town.  Now that
-        * there's an atexit callback to prevent third-party code from breaking
-        * things by calling exit() directly, we have to reset the callbacks
-        * explicitly to make this work as intended.
-        */
-       on_exit_reset();
-
-       /*
-        * Note we do exit(2) not exit(0).  This is to force the postmaster into a
-        * system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
+        * We DO NOT want to run proc_exit() or atexit() callbacks -- we're here
+        * because shared memory may be corrupted, so we don't want to try to
+        * clean up our transaction.  Just nail the windows shut and get out of
+        * town.  The callbacks wouldn't be safe to run from a signal handler,
+        * anyway.
+        *
+        * Note we do _exit(2) not _exit(0).  This is to force the postmaster into
+        * a system reset cycle if someone sends a manual SIGQUIT to a random
         * backend.  This is necessary precisely because we don't clean up our
         * shared memory state.  (The "dead man switch" mechanism in pmsignal.c
         * should ensure the postmaster sees this as a crash, too, but no harm in
         * being doubly sure.)
         */
-       exit(2);
+       _exit(2);
 }
 
 /* SIGHUP: set flag to re-read config file at next convenient time */
index 600f5d53ebaf062bb8de25751019181b94e8efca..7a198910c4aa016b828694daf5d68c8203a21c88 100644 (file)
@@ -770,27 +770,21 @@ WalRcvShutdownHandler(SIGNAL_ARGS)
 static void
 WalRcvQuickDieHandler(SIGNAL_ARGS)
 {
-       PG_SETMASK(&BlockSig);
-
        /*
-        * We DO NOT want to run proc_exit() callbacks -- we're here because
-        * shared memory may be corrupted, so we don't want to try to clean up our
-        * transaction.  Just nail the windows shut and get out of town.  Now that
-        * there's an atexit callback to prevent third-party code from breaking
-        * things by calling exit() directly, we have to reset the callbacks
-        * explicitly to make this work as intended.
-        */
-       on_exit_reset();
-
-       /*
-        * Note we do exit(2) not exit(0).  This is to force the postmaster into a
-        * system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
-        * backend.  This is necessary precisely because we don't clean up our
-        * shared memory state.  (The "dead man switch" mechanism in pmsignal.c
-        * should ensure the postmaster sees this as a crash, too, but no harm in
-        * being doubly sure.)
+        * We DO NOT want to run proc_exit() or atexit() callbacks -- we're here
+        * because shared memory may be corrupted, so we don't want to try to
+        * clean up our transaction.  Just nail the windows shut and get out of
+        * town.  The callbacks wouldn't be safe to run from a signal handler,
+        * anyway.
+        *
+        * Note we use _exit(2) not _exit(0).  This is to force the postmaster
+        * into a system reset cycle if someone sends a manual SIGQUIT to a
+        * random backend.  This is necessary precisely because we don't clean up
+        * our shared memory state.  (The "dead man switch" mechanism in
+        * pmsignal.c should ensure the postmaster sees this as a crash, too, but
+        * no harm in being doubly sure.)
         */
-       exit(2);
+       _exit(2);
 }
 
 /*
index 12c31fd2f69ae7e2ad2710140cd1171a16beb4df..8c5656896c1ecf1481bcab76bca7626e4daa8498 100644 (file)
@@ -2590,6 +2590,16 @@ quickdie(SIGNAL_ARGS)
                whereToSendOutput = DestNone;
 
        /*
+        * Notify the client before exiting, to give a clue on what happened.
+        *
+        * It's dubious to call ereport() from a signal handler.  It is certainly
+        * not async-signal safe.  But it seems better to try, than to disconnect
+        * abruptly and leave the client wondering what happened.  It's remotely
+        * possible that we crash or hang while trying to send the message, but
+        * receiving a SIGQUIT is a sign that something has already gone badly
+        * wrong, so there's not much to lose.  Assuming the postmaster is still
+        * running, it will SIGKILL us soon if we get stuck for some reason.
+        *
         * Ideally this should be ereport(FATAL), but then we'd not get control
         * back...
         */
@@ -2604,24 +2614,20 @@ quickdie(SIGNAL_ARGS)
                                         " database and repeat your command.")));
 
        /*
-        * We DO NOT want to run proc_exit() callbacks -- we're here because
-        * shared memory may be corrupted, so we don't want to try to clean up our
-        * transaction.  Just nail the windows shut and get out of town.  Now that
-        * there's an atexit callback to prevent third-party code from breaking
-        * things by calling exit() directly, we have to reset the callbacks
-        * explicitly to make this work as intended.
-        */
-       on_exit_reset();
-
-       /*
-        * Note we do exit(2) not exit(0).  This is to force the postmaster into a
-        * system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
+        * We DO NOT want to run proc_exit() or atexit() callbacks -- we're here
+        * because shared memory may be corrupted, so we don't want to try to
+        * clean up our transaction.  Just nail the windows shut and get out of
+        * town.  The callbacks wouldn't be safe to run from a signal handler,
+        * anyway.
+        *
+        * Note we do _exit(2) not _exit(0).  This is to force the postmaster into
+        * a system reset cycle if someone sends a manual SIGQUIT to a random
         * backend.  This is necessary precisely because we don't clean up our
         * shared memory state.  (The "dead man switch" mechanism in pmsignal.c
         * should ensure the postmaster sees this as a crash, too, but no harm in
         * being doubly sure.)
         */
-       exit(2);
+       _exit(2);
 }
 
 /*