summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2008-11-26 00:26:23 +0000
committerTom Lane2008-11-26 00:26:23 +0000
commit09611425dc1f41fec231f8061a50459595124bba (patch)
tree6452fc4e80c25da05a549cfcb791defc4ce80262
parentfffe0b039ab452295953c9d30956bf24ce2fef63 (diff)
Adjust the behavior of the PQExpBuffer code to make it have well-defined
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.
-rw-r--r--src/bin/psql/input.c9
-rw-r--r--src/bin/psql/mainloop.c35
-rw-r--r--src/bin/psql/psqlscan.l6
-rw-r--r--src/interfaces/libpq/fe-connect.c8
-rw-r--r--src/interfaces/libpq/pqexpbuffer.c63
-rw-r--r--src/interfaces/libpq/pqexpbuffer.h18
6 files changed, 118 insertions, 21 deletions
diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c
index e068a8923f..f854d7c109 100644
--- a/src/bin/psql/input.c
+++ b/src/bin/psql/input.c
@@ -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')
{
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
index 20cc913dbc..7f2c0fa079 100644
--- a/src/bin/psql/mainloop.c
+++ b/src/bin/psql/mainloop.c
@@ -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 */
diff --git a/src/bin/psql/psqlscan.l b/src/bin/psql/psqlscan.l
index 31c83af641..f18bd81e3a 100644
--- a/src/bin/psql/psqlscan.l
+++ b/src/bin/psql/psqlscan.l
@@ -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);
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 5b8e84744f..1b6561886b 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -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)
diff --git a/src/interfaces/libpq/pqexpbuffer.c b/src/interfaces/libpq/pqexpbuffer.c
index 1e6f905313..8ff9984026 100644
--- a/src/interfaces/libpq/pqexpbuffer.c
+++ b/src/interfaces/libpq/pqexpbuffer.c
@@ -32,6 +32,32 @@
#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 (;;)
{
/*
diff --git a/src/interfaces/libpq/pqexpbuffer.h b/src/interfaces/libpq/pqexpbuffer.h
index eba8b5b662..6c3705b49e 100644
--- a/src/interfaces/libpq/pqexpbuffer.h
+++ b/src/interfaces/libpq/pqexpbuffer.h
@@ -35,6 +35,10 @@
* 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
@@ -47,6 +51,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
* be returned by PQrequestCancel().
@@ -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);