]> granicus.if.org Git - postgresql/commitdiff
Adjust the behavior of the PQExpBuffer code to make it have well-defined
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 26 Nov 2008 00:26:23 +0000 (00:26 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 26 Nov 2008 00:26:23 +0000 (00:26 +0000)
results (ie, an empty "broken" buffer) if memory overrun occurs anywhere
along the way to filling the buffer.  The previous coding would just silently
discard portions of the intended buffer contents, as exhibited in trouble
report from Sam Mason.  Also, tweak psql's main loop to correctly detect
and report such overruns.  There's probably much more that should be done
in this line, but this is a start.

src/bin/psql/input.c
src/bin/psql/mainloop.c
src/bin/psql/psqlscan.l
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/pqexpbuffer.c
src/interfaces/libpq/pqexpbuffer.h

index 6a12dc9ff51ab1c8668cdc4ea2641b0abc072af9..5865a2e4b6efb3fe1e96308e05dc3eb20fad7f7a 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2008, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.64 2008/01/01 19:45:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.65 2008/11/26 00:26:23 tgl Exp $
  */
 #include "postgres_fe.h"
 
@@ -184,7 +184,8 @@ gets_fromFile(FILE *source)
                {
                        if (ferror(source))
                        {
-                               psql_error("could not read from input file: %s\n", strerror(errno));
+                               psql_error("could not read from input file: %s\n",
+                                                  strerror(errno));
                                return NULL;
                        }
                        break;
@@ -192,6 +193,12 @@ gets_fromFile(FILE *source)
 
                appendPQExpBufferStr(buffer, line);
 
+               if (PQExpBufferBroken(buffer))
+               {
+                       psql_error("out of memory\n");
+                       return NULL;
+               }
+
                /* EOL? */
                if (buffer->data[buffer->len - 1] == '\n')
                {
index 526674c8181dd2ebcf5e0be775af219f4bdbccbb..16532e2d420c28f29abbedeb3370d5a3d342019e 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2008, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.92 2008/06/10 20:58:19 neilc Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.93 2008/11/26 00:26:23 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "mainloop.h"
@@ -26,9 +26,9 @@ int
 MainLoop(FILE *source)
 {
        PsqlScanState scan_state;       /* lexer working state */
-       PQExpBuffer query_buf;          /* buffer for query being accumulated */
-       PQExpBuffer previous_buf;       /* if there isn't anything in the new buffer
-                                                                * yet, use this one for \e, etc. */
+       volatile PQExpBuffer query_buf; /* buffer for query being accumulated */
+       volatile PQExpBuffer previous_buf;      /* if there isn't anything in the new
+                                                                * buffer yet, use this one for \e, etc. */
        PQExpBuffer history_buf;        /* earlier lines of a multi-line command, not
                                                                 * yet saved to readline history */
        char       *line;                       /* current line of input */
@@ -62,7 +62,9 @@ MainLoop(FILE *source)
        query_buf = createPQExpBuffer();
        previous_buf = createPQExpBuffer();
        history_buf = createPQExpBuffer();
-       if (!query_buf || !previous_buf || !history_buf)
+       if (PQExpBufferBroken(query_buf) ||
+               PQExpBufferBroken(previous_buf) ||
+               PQExpBufferBroken(history_buf))
        {
                psql_error("out of memory\n");
                exit(EXIT_FAILURE);
@@ -220,6 +222,12 @@ MainLoop(FILE *source)
                        scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
                        prompt_status = prompt_tmp;
 
+                       if (PQExpBufferBroken(query_buf))
+                       {
+                               psql_error("out of memory\n");
+                               exit(EXIT_FAILURE);
+                       }
+
                        /*
                         * Send command if semicolon found, or if end of line and we're in
                         * single-line mode.
@@ -242,9 +250,15 @@ MainLoop(FILE *source)
                                success = SendQuery(query_buf->data);
                                slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
 
-                               resetPQExpBuffer(previous_buf);
-                               appendPQExpBufferStr(previous_buf, query_buf->data);
+                               /* transfer query to previous_buf by pointer-swapping */
+                               {
+                                       PQExpBuffer swap_buf = previous_buf;
+
+                                       previous_buf = query_buf;
+                                       query_buf = swap_buf;
+                               }
                                resetPQExpBuffer(query_buf);
+
                                added_nl_pos = -1;
                                /* we need not do psql_scan_reset() here */
                        }
@@ -294,8 +308,13 @@ MainLoop(FILE *source)
                                {
                                        success = SendQuery(query_buf->data);
 
-                                       resetPQExpBuffer(previous_buf);
-                                       appendPQExpBufferStr(previous_buf, query_buf->data);
+                                       /* transfer query to previous_buf by pointer-swapping */
+                                       {
+                                               PQExpBuffer swap_buf = previous_buf;
+
+                                               previous_buf = query_buf;
+                                               query_buf = swap_buf;
+                                       }
                                        resetPQExpBuffer(query_buf);
 
                                        /* flush any paren nesting info after forced send */
index 02329b9e7578fe97a6a1ae2db3568f01b2653c0c..45dfba8d665515d7293af7410b559c61adbb1a97 100644 (file)
@@ -33,7 +33,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.26 2008/10/29 08:04:53 petere Exp $
+ *       $PostgreSQL: pgsql/src/bin/psql/psqlscan.l,v 1.27 2008/11/26 00:26:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1453,6 +1453,12 @@ psql_scan_slash_option(PsqlScanState state,
                                        error = true;
                                }
 
+                               if (PQExpBufferBroken(&output))
+                               {
+                                       psql_error("%s: out of memory\n", cmd);
+                                       error = true;
+                               }
+
                                /* Now done with cmd, transfer result to mybuf */
                                resetPQExpBuffer(&mybuf);
 
index 2cfe36e3a22052988903302617fcec1ec965312a..ce6af2bcd328f4149dbd4409e351843a68a51dd8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.369 2008/11/25 19:30:42 tgl Exp $
+ *       $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.370 2008/11/26 00:26:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -585,7 +585,7 @@ PQconndefaults(void)
        PQconninfoOption *connOptions;
 
        initPQExpBuffer(&errorBuf);
-       if (errorBuf.data == NULL)
+       if (PQExpBufferBroken(&errorBuf))
                return NULL;                    /* out of memory already :-( */
        connOptions = conninfo_parse("", &errorBuf, true);
        termPQExpBuffer(&errorBuf);
@@ -1970,8 +1970,8 @@ makeEmptyPGconn(void)
 
        if (conn->inBuffer == NULL ||
                conn->outBuffer == NULL ||
-               conn->errorMessage.data == NULL ||
-               conn->workBuffer.data == NULL)
+               PQExpBufferBroken(&conn->errorMessage) ||
+               PQExpBufferBroken(&conn->workBuffer))
        {
                /* out of memory already :-( */
                freePGconn(conn);
@@ -3151,7 +3151,7 @@ PQconninfoParse(const char *conninfo, char **errmsg)
        if (errmsg)
                *errmsg = NULL;                 /* default */
        initPQExpBuffer(&errorBuf);
-       if (errorBuf.data == NULL)
+       if (PQExpBufferBroken(&errorBuf))
                return NULL;                    /* out of memory already :-( */
        connOptions = conninfo_parse(conninfo, &errorBuf, false);
        if (connOptions == NULL && errmsg)
index c8e80498d1f1c817708c68f7fcaa578760507fb0..aeb3a21a387cc69e1eebc3dc226322e9ec81b938 100644 (file)
@@ -17,7 +17,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/pqexpbuffer.c,v 1.24 2008/01/01 19:46:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/pqexpbuffer.c,v 1.25 2008/11/26 00:26:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "win32.h"
 #endif
 
+
+/* All "broken" PQExpBuffers point to this string. */
+static const char oom_buffer[1] = "";
+
+
+/*
+ * markPQExpBufferBroken
+ *
+ * Put a PQExpBuffer in "broken" state if it isn't already.
+ */
+static void
+markPQExpBufferBroken(PQExpBuffer str)
+{
+       if (str->data != oom_buffer)
+               free(str->data);
+       /*
+        * Casting away const here is a bit ugly, but it seems preferable to
+        * not marking oom_buffer const.  We want to do that to encourage the
+        * compiler to put oom_buffer in read-only storage, so that anyone who
+        * tries to scribble on a broken PQExpBuffer will get a failure.
+        */
+       str->data = (char *) oom_buffer;
+       str->len = 0;
+       str->maxlen = 0;
+}
+
 /*
  * createPQExpBuffer
  *
@@ -61,6 +87,7 @@ initPQExpBuffer(PQExpBuffer str)
        str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
        if (str->data == NULL)
        {
+               str->data = (char *) oom_buffer;                /* see comment above */
                str->maxlen = 0;
                str->len = 0;
        }
@@ -96,12 +123,10 @@ destroyPQExpBuffer(PQExpBuffer str)
 void
 termPQExpBuffer(PQExpBuffer str)
 {
-       if (str->data)
-       {
+       if (str->data != oom_buffer)
                free(str->data);
-               str->data = NULL;
-       }
        /* just for luck, make the buffer validly empty. */
+       str->data = (char *) oom_buffer;                /* see comment above */
        str->maxlen = 0;
        str->len = 0;
 }
@@ -109,15 +134,24 @@ termPQExpBuffer(PQExpBuffer str)
 /*
  * resetPQExpBuffer
  *             Reset a PQExpBuffer to empty
+ *
+ * Note: if possible, a "broken" PQExpBuffer is returned to normal.
  */
 void
 resetPQExpBuffer(PQExpBuffer str)
 {
        if (str)
        {
-               str->len = 0;
-               if (str->data)
+               if (str->data != oom_buffer)
+               {
+                       str->len = 0;
                        str->data[0] = '\0';
+               }
+               else
+               {
+                       /* try to reinitialize to valid state */
+                       initPQExpBuffer(str);
+               }
        }
 }
 
@@ -126,7 +160,8 @@ resetPQExpBuffer(PQExpBuffer str)
  * Make sure there is enough space for 'needed' more bytes in the buffer
  * ('needed' does not include the terminating null).
  *
- * Returns 1 if OK, 0 if failed to enlarge buffer.
+ * Returns 1 if OK, 0 if failed to enlarge buffer.  (In the latter case
+ * the buffer is left in "broken" state.)
  */
 int
 enlargePQExpBuffer(PQExpBuffer str, size_t needed)
@@ -134,13 +169,19 @@ enlargePQExpBuffer(PQExpBuffer str, size_t needed)
        size_t          newlen;
        char       *newdata;
 
+       if (PQExpBufferBroken(str))
+               return 0;                               /* already failed */
+
        /*
         * Guard against ridiculous "needed" values, which can occur if we're fed
         * bogus data.  Without this, we can get an overflow or infinite loop in
         * the following.
         */
        if (needed >= ((size_t) INT_MAX - str->len))
+       {
+               markPQExpBufferBroken(str);
                return 0;
+       }
 
        needed += str->len + 1;         /* total space required now */
 
@@ -173,6 +214,8 @@ enlargePQExpBuffer(PQExpBuffer str, size_t needed)
                str->maxlen = newlen;
                return 1;
        }
+
+       markPQExpBufferBroken(str);
        return 0;
 }
 
@@ -192,6 +235,9 @@ printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
 
        resetPQExpBuffer(str);
 
+       if (PQExpBufferBroken(str))
+               return;                                 /* already failed */
+
        for (;;)
        {
                /*
@@ -240,6 +286,9 @@ appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
        size_t          avail;
        int                     nprinted;
 
+       if (PQExpBufferBroken(str))
+               return;                                 /* already failed */
+
        for (;;)
        {
                /*
index b834ac48082e84cc3cb820833a849bd6af8026fe..3acca24b56967dfa740230ef8fde3eea3bdd0e4c 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/pqexpbuffer.h,v 1.19 2008/01/01 19:46:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/pqexpbuffer.h,v 1.20 2008/11/26 00:26:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  *                             string size (including the terminating '\0' char) that we can
  *                             currently store in 'data' without having to reallocate
  *                             more space.  We must always have maxlen > len.
+ *
+ * An exception occurs if we failed to allocate enough memory for the string
+ * buffer.  In that case data points to a statically allocated empty string,
+ * and len = maxlen = 0.
  *-------------------------
  */
 typedef struct PQExpBufferData
@@ -46,6 +50,15 @@ typedef struct PQExpBufferData
 
 typedef PQExpBufferData *PQExpBuffer;
 
+/*------------------------
+ * Test for a broken (out of memory) PQExpBuffer.
+ * When a buffer is "broken", all operations except resetting or deleting it
+ * are no-ops.
+ *------------------------
+ */
+#define PQExpBufferBroken(str)  \
+       (!(str) || (str)->maxlen == 0)
+
 /*------------------------
  * Initial size of the data buffer in a PQExpBuffer.
  * NB: this must be large enough to hold error messages that might
@@ -103,6 +116,8 @@ extern void termPQExpBuffer(PQExpBuffer str);
 /*------------------------
  * resetPQExpBuffer
  *             Reset a PQExpBuffer to empty
+ *
+ * Note: if possible, a "broken" PQExpBuffer is returned to normal.
  */
 extern void resetPQExpBuffer(PQExpBuffer str);
 
@@ -111,7 +126,8 @@ extern void resetPQExpBuffer(PQExpBuffer str);
  * Make sure there is enough space for 'needed' more bytes in the buffer
  * ('needed' does not include the terminating null).
  *
- * Returns 1 if OK, 0 if failed to enlarge buffer.
+ * Returns 1 if OK, 0 if failed to enlarge buffer.  (In the latter case
+ * the buffer is left in "broken" state.)
  */
 extern int     enlargePQExpBuffer(PQExpBuffer str, size_t needed);