diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c index 6a12dc9ff5..5865a2e4b6 100644 --- a/src/bin/psql/input.c +++ b/src/bin/psql/input.c @@ -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') { diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c index 526674c818..16532e2d42 100644 --- a/src/bin/psql/mainloop.c +++ b/src/bin/psql/mainloop.c @@ -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 */ diff --git a/src/bin/psql/psqlscan.l b/src/bin/psql/psqlscan.l index 02329b9e75..45dfba8d66 100644 --- a/src/bin/psql/psqlscan.l +++ b/src/bin/psql/psqlscan.l @@ -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); diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 2cfe36e3a2..ce6af2bcd3 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -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) diff --git a/src/interfaces/libpq/pqexpbuffer.c b/src/interfaces/libpq/pqexpbuffer.c index c8e80498d1..aeb3a21a38 100644 --- a/src/interfaces/libpq/pqexpbuffer.c +++ b/src/interfaces/libpq/pqexpbuffer.c @@ -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 $ * *------------------------------------------------------------------------- */ @@ -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 b834ac4808..3acca24b56 100644 --- a/src/interfaces/libpq/pqexpbuffer.h +++ b/src/interfaces/libpq/pqexpbuffer.h @@ -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 $ * *------------------------------------------------------------------------- */ @@ -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 @@ -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);