]> granicus.if.org Git - postgresql/commitdiff
Fix possible crashes due to using elog/ereport too early in startup.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 11 Jan 2014 21:35:34 +0000 (16:35 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 11 Jan 2014 21:35:34 +0000 (16:35 -0500)
Per reports from Andres Freund and Luke Campbell, a server failure during
set_pglocale_pgservice results in a segfault rather than a useful error
message, because the infrastructure needed to use ereport hasn't been
initialized; specifically, MemoryContextInit hasn't been called.
One known cause of this is starting the server in a directory it
doesn't have permission to read.

We could try to prevent set_pglocale_pgservice from using anything that
depends on palloc or elog, but that would be messy, and the odds of future
breakage seem high.  Moreover there are other things being called in main.c
that look likely to use palloc or elog too --- perhaps those things
shouldn't be there, but they are there today.  The best solution seems to
be to move the call of MemoryContextInit to very early in the backend's
real main() function.  I've verified that an elog or ereport occurring
immediately after that is now capable of sending something useful to
stderr.

I also added code to elog.c to print something intelligible rather than
just crashing if MemoryContextInit hasn't created the ErrorContext.
This could happen if MemoryContextInit itself fails (due to malloc
failure), and provides some future-proofing against someone trying to
sneak in new code even earlier in server startup.

Back-patch to all supported branches.  Since we've only heard reports of
this type of failure recently, it may be that some recent change has made
it more likely to see a crash of this kind; but it sure looks like it's
broken all the way back.

src/backend/bootstrap/bootstrap.c
src/backend/main/main.c
src/backend/postmaster/postmaster.c
src/backend/tcop/postgres.c
src/backend/utils/error/elog.c
src/backend/utils/mmgr/mcxt.c

index b2b385e0cabe1f39616cbfd1930b3eb6e7bef6ae..6f7c18cd24b3c408ef340239944a7cb7f864b79b 100644 (file)
@@ -198,14 +198,6 @@ AuxiliaryProcessMain(int argc, char *argv[])
 
        MyStartTime = time(NULL);
 
-       /*
-        * Fire up essential subsystems: error and memory management
-        *
-        * If we are running under the postmaster, this is done already.
-        */
-       if (!IsUnderPostmaster)
-               MemoryContextInit();
-
        /* Compute paths, if we didn't inherit them from postmaster */
        if (my_exec_path[0] == '\0')
        {
index e0183029e9f7de9557afbb3336a28a974a6e3c99..fc5e7c396b6ecd5f1c1154b3e90b8af87e00a566 100644 (file)
@@ -39,6 +39,7 @@
 #include "postmaster/postmaster.h"
 #include "tcop/tcopprot.h"
 #include "utils/help_config.h"
+#include "utils/memutils.h"
 #include "utils/pg_locale.h"
 #include "utils/ps_status.h"
 #ifdef WIN32
@@ -89,6 +90,15 @@ main(int argc, char *argv[])
        pgwin32_install_crashdump_handler();
 #endif
 
+       /*
+        * Fire up essential subsystems: error and memory management
+        *
+        * Code after this point is allowed to use elog/ereport, though
+        * localization of messages may not work right away, and messages won't go
+        * anywhere but stderr until GUC settings get loaded.
+        */
+       MemoryContextInit();
+
        /*
         * Set up locale information from environment.  Note that LC_CTYPE and
         * LC_COLLATE will be overridden later from pg_control if we are in an
index 437b7fae6deddaf98b6b973e6b66bd2d8a83b58f..b96b505834a19e9888c2ae7d2ffb4d8341d43a6f 100644 (file)
@@ -512,11 +512,6 @@ PostmasterMain(int argc, char *argv[])
         */
        umask(S_IRWXG | S_IRWXO);
 
-       /*
-        * Fire up essential subsystems: memory management
-        */
-       MemoryContextInit();
-
        /*
         * By default, palloc() requests in the postmaster will be allocated in
         * the PostmasterContext, which is space that can be recycled by backends.
@@ -3991,7 +3986,6 @@ SubPostmasterMain(int argc, char *argv[])
        whereToSendOutput = DestNone;
 
        /* Setup essential subsystems (to ensure elog() behaves sanely) */
-       MemoryContextInit();
        InitializeGUCOptions();
 
        /* Read in the variables file */
index 470184bc5bbe96cc8c0f66cc283e42a3e781c126..8b76af89a3406f9e42a189f5cbe1081a41b1c66e 100644 (file)
@@ -3525,14 +3525,6 @@ PostgresMain(int argc, char *argv[],
                MyStartTime = time(NULL);
        }
 
-       /*
-        * Fire up essential subsystems: error and memory management
-        *
-        * If we are running under the postmaster, this is done already.
-        */
-       if (!IsUnderPostmaster)
-               MemoryContextInit();
-
        SetProcessingMode(InitProcessing);
 
        /* Compute paths, if we didn't inherit them from postmaster */
index 831fe07120f6beb63d4ee7f13587e43a830be00f..f5dfe64df2ea07f637f5acd21e1493bdca0c13d2 100644 (file)
@@ -311,6 +311,18 @@ errstart(int elevel, const char *filename, int lineno,
        if (elevel < ERROR && !output_to_server && !output_to_client)
                return false;
 
+       /*
+        * We need to do some actual work.  Make sure that memory context
+        * initialization has finished, else we can't do anything useful.
+        */
+       if (ErrorContext == NULL)
+       {
+               /* Ooops, hard crash time; very little we can do safely here */
+               write_stderr("error occurred at %s:%d before error message processing is available\n",
+                                        filename ? filename : "(unknown file)", lineno);
+               exit(2);
+       }
+
        /*
         * Okay, crank up a stack entry to store the info in.
         */
@@ -1147,6 +1159,15 @@ elog_start(const char *filename, int lineno, const char *funcname)
 {
        ErrorData  *edata;
 
+       /* Make sure that memory context initialization has finished */
+       if (ErrorContext == NULL)
+       {
+               /* Ooops, hard crash time; very little we can do safely here */
+               write_stderr("error occurred at %s:%d before error message processing is available\n",
+                                        filename ? filename : "(unknown file)", lineno);
+               exit(2);
+       }
+
        if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
        {
                /*
index 466d53d57ef555cc717ee6b57b627a084d70ea46..024f2cd65b0ea74650396037bacbf39648d6254a 100644 (file)
@@ -68,7 +68,8 @@ static void MemoryContextStatsInternal(MemoryContext context, int level);
  * In normal multi-backend operation, this is called once during
  * postmaster startup, and not at all by individual backend startup
  * (since the backends inherit an already-initialized context subsystem
- * by virtue of being forked off the postmaster).
+ * by virtue of being forked off the postmaster).  But in an EXEC_BACKEND
+ * build, each process must do this for itself.
  *
  * In a standalone backend this must be called during backend startup.
  */
@@ -102,6 +103,9 @@ MemoryContextInit(void)
         * where retained memory in a context is *essential* --- we want to be
         * sure ErrorContext still has some memory even if we've run out
         * elsewhere!
+        *
+        * This should be the last step in this function, as elog.c assumes memory
+        * management works once ErrorContext is non-null.
         */
        ErrorContext = AllocSetContextCreate(TopMemoryContext,
                                                                                 "ErrorContext",