From 72a5db15d190121e63126055824f38dd062428be Mon Sep 17 00:00:00 2001 From: Neil Conway Date: Sun, 12 Jun 2005 00:00:21 +0000 Subject: [PATCH] libpq was not consistently checking for memory allocation failures. This patch adds missing checks to the call sites of malloc(), strdup(), PQmakeEmptyPGresult(), pqResultAlloc(), and pqResultStrdup(), and updates the documentation. Per original report from Volkan Yazici about PQmakeEmptyPGresult() not checking for malloc() failure. --- doc/src/sgml/libpq.sgml | 55 +++++++++--------- src/interfaces/libpq/fe-connect.c | 67 ++++++++++++++-------- src/interfaces/libpq/fe-exec.c | 29 ++++++---- src/interfaces/libpq/fe-misc.c | 5 +- src/interfaces/libpq/fe-print.c | 20 +++++-- src/interfaces/libpq/fe-protocol2.c | 57 +++++++++++++------ src/interfaces/libpq/fe-protocol3.c | 86 ++++++++++++++++++----------- src/interfaces/libpq/libpq-int.h | 4 +- 8 files changed, 206 insertions(+), 117 deletions(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index be92de3e70..fe15c2b1bb 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1,5 +1,5 @@ @@ -581,14 +581,15 @@ typedef struct - Returns a connection options array. This may - be used to determine all possible PQconnectdb options and their + Returns a connection options array. This may be used to determine + all possible PQconnectdb options and their current default values. The return value points to an array of - PQconninfoOption structures, which ends with an entry having a null - keyword pointer. Note that the current default values - (val fields) - will depend on environment variables and other context. - Callers must treat the connection options data as read-only. + PQconninfoOption structures, which ends + with an entry having a null keyword pointer. The + null pointer is returned if memory could not be allocated. Note that + the current default values (val fields) + will depend on environment variables and other context. Callers + must treat the connection options data as read-only. @@ -1651,18 +1652,22 @@ void PQclear(PGresult *res); Constructs an empty PGresult object with the given status. -PGresult* PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status); +PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status); -This is libpq's internal function to allocate and initialize an empty -PGresult object. It is exported because some applications find it -useful to generate result objects (particularly objects with error -status) themselves. If conn is not null and status indicates an error, -the current error message of the specified connection is copied into the PGresult. -Note that PQclear should eventually be called on the object, just -as with a PGresult returned by libpq itself. +This is libpq's internal function to allocate and +initialize an empty PGresult object. This +function returns NULL if memory could not be allocated. It is exported +because some applications find it useful to generate result objects +(particularly objects with error status) themselves. If +conn is not null and status +indicates an error, the current error message of the specified +connection is copied into the PGresult. Note +that PQclear should eventually be called on the +object, just as with a PGresult returned by +libpq itself. @@ -2266,15 +2271,15 @@ unsigned char *PQescapeBytea(const unsigned char *from, PQescapeBytea returns an escaped version of the from parameter binary string in memory - allocated with malloc(). This memory must be freed - using PQfreemem when the result is no longer needed. - The return string has all special characters replaced so that they - can be properly processed by the - PostgreSQL string literal parser, and - the bytea input function. A terminating zero byte is - also added. The single quotes that must surround - PostgreSQL string literals are not part - of the result string. + allocated with malloc() (a null pointer is returned if + memory could not be allocated). This memory must be freed using + PQfreemem when the result is no longer needed. The + return string has all special characters replaced so that they can + be properly processed by the PostgreSQL + string literal parser, and the bytea input function. A + terminating zero byte is also added. The single quotes that must + surround PostgreSQL string literals are + not part of the result string. diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 979f5cd073..13958b9ad3 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.309 2005/06/10 04:01:36 momjian Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.310 2005/06/12 00:00:21 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -368,6 +368,8 @@ connectOptions1(PGconn *conn, const char *conninfo) * * Don't put anything cute here --- intelligence should be in * connectOptions2 ... + * + * XXX: probably worth checking strdup() return value here... */ tmp = conninfo_getval(connOptions, "hostaddr"); conn->pghostaddr = tmp ? strdup(tmp) : NULL; @@ -459,7 +461,6 @@ connectOptions2(PGconn *conn) } #ifdef NOT_USED - /* * parse dbName to get all additional info in it, if any */ @@ -2167,11 +2168,9 @@ closePGconn(PGconn *conn) } /* - PQfinish: - properly close a connection to the backend - also frees the PGconn data structure so it shouldn't be re-used - after this -*/ + * PQfinish: properly close a connection to the backend. Also frees + * the PGconn data structure so it shouldn't be re-used after this. + */ void PQfinish(PGconn *conn) { @@ -2182,10 +2181,10 @@ PQfinish(PGconn *conn) } } -/* PQreset : - resets the connection to the backend - closes the existing connection and makes a new one -*/ +/* + * PQreset: resets the connection to the backend by closing the + * existing connection and creating a new one. + */ void PQreset(PGconn *conn) { @@ -2199,11 +2198,12 @@ PQreset(PGconn *conn) } -/* PQresetStart : - resets the connection to the backend - closes the existing connection and makes a new one - Returns 1 on success, 0 on failure. -*/ +/* + * PQresetStart: + * resets the connection to the backend + * closes the existing connection and makes a new one + * Returns 1 on success, 0 on failure. + */ int PQresetStart(PGconn *conn) { @@ -2218,11 +2218,11 @@ PQresetStart(PGconn *conn) } -/* PQresetPoll : - resets the connection to the backend - closes the existing connection and makes a new one -*/ - +/* + * PQresetPoll: + * resets the connection to the backend + * closes the existing connection and makes a new one + */ PostgresPollingStatusType PQresetPoll(PGconn *conn) { @@ -2514,7 +2514,7 @@ parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage) * location to find our config files. */ snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf", - getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR); + getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR); if (service != NULL) { @@ -2802,7 +2802,14 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage) if (option->val) free(option->val); option->val = strdup(pval); - + if (!option->val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + free(buf); + return NULL; + } } /* Done with the modifiable input string */ @@ -2835,6 +2842,13 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage) if ((tmp = getenv(option->envvar)) != NULL) { option->val = strdup(tmp); + if (!option->val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + return NULL; + } continue; } } @@ -2846,6 +2860,13 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage) if (option->compiled != NULL) { option->val = strdup(option->compiled); + if (!option->val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + return NULL; + } continue; } diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 1a01a4c627..64ef9cd428 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.168 2005/06/09 20:01:16 tgl Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.169 2005/06/12 00:00:21 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -134,6 +134,8 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) PGresult *result; result = (PGresult *) malloc(sizeof(PGresult)); + if (!result) + return NULL; result->ntups = 0; result->numAttributes = 0; @@ -453,7 +455,7 @@ pqPrepareAsyncResult(PGconn *conn) * a trailing newline, and should not be more than one line). */ void -pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...) +pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt, ...) { char msgBuf[1024]; va_list args; @@ -470,6 +472,8 @@ pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...) /* Make a PGresult to pass to the notice receiver */ res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR); + if (!res) + return; res->noticeHooks = *hooks; /* @@ -480,15 +484,19 @@ pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...) /* XXX should provide a SQLSTATE too? */ /* - * Result text is always just the primary message + newline. + * Result text is always just the primary message + newline. If we + * can't allocate it, don't bother invoking the receiver. */ res->errMsg = (char *) pqResultAlloc(res, strlen(msgBuf) + 2, FALSE); - sprintf(res->errMsg, "%s\n", msgBuf); + if (res->errMsg) + { + sprintf(res->errMsg, "%s\n", msgBuf); - /* - * Pass to receiver, then free it. - */ - (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); + /* + * Pass to receiver, then free it. + */ + (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); + } PQclear(res); } @@ -1127,8 +1135,9 @@ PQisBusy(PGconn *conn) /* * PQgetResult - * Get the next PGresult produced by a query. - * Returns NULL if and only if no query work remains. + * Get the next PGresult produced by a query. Returns NULL if no + * query work remains or an error has occurred (e.g. out of + * memory). */ PGresult * diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index f3ed41423e..66ad325c52 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -23,7 +23,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/interfaces/libpq/fe-misc.c,v 1.113 2005/02/22 04:42:20 momjian Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/fe-misc.c,v 1.114 2005/06/12 00:00:21 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -175,7 +175,8 @@ pqGetnchar(char *s, size_t len, PGconn *conn) conn->inCursor += len; if (conn->Pfdebug) - fprintf(conn->Pfdebug, libpq_gettext("From backend (%lu)> %.*s\n"), (unsigned long) len, (int) len, s); + fprintf(conn->Pfdebug, libpq_gettext("From backend (%lu)> %.*s\n"), + (unsigned long) len, (int) len, s); return 0; } diff --git a/src/interfaces/libpq/fe-print.c b/src/interfaces/libpq/fe-print.c index e6740a3d18..762383feba 100644 --- a/src/interfaces/libpq/fe-print.c +++ b/src/interfaces/libpq/fe-print.c @@ -10,7 +10,7 @@ * didn't really belong there. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/interfaces/libpq/fe-print.c,v 1.59 2005/02/22 04:42:20 momjian Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/fe-print.c,v 1.60 2005/06/12 00:00:21 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -62,14 +62,11 @@ static void fill(int length, int max, char filler, FILE *fp); * details * * This function should probably be removed sometime since psql - * doesn't use it anymore. It is unclear to what extend this is used + * doesn't use it anymore. It is unclear to what extent this is used * by external clients, however. */ - void -PQprint(FILE *fout, - const PGresult *res, - const PQprintOpt *po) +PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po) { int nFields; @@ -611,6 +608,12 @@ PQdisplayTuples(const PGresult *res, if (fillAlign) { fLength = (int *) malloc(nFields * sizeof(int)); + if (!fLength) + { + fprintf(stderr, libpq_gettext("out of memory\n")); + exit(1); + } + for (j = 0; j < nFields; j++) { fLength[j] = strlen(PQfname(res, j)); @@ -705,6 +708,11 @@ PQprintTuples(const PGresult *res, width = nFields * 14; tborder = malloc(width + 1); + if (!tborder) + { + fprintf(stderr, libpq_gettext("out of memory\n")); + exit(1); + } for (i = 0; i <= width; i++) tborder[i] = '-'; tborder[i] = '\0'; diff --git a/src/interfaces/libpq/fe-protocol2.c b/src/interfaces/libpq/fe-protocol2.c index 2501ef4a60..b5753dba44 100644 --- a/src/interfaces/libpq/fe-protocol2.c +++ b/src/interfaces/libpq/fe-protocol2.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol2.c,v 1.17 2005/05/11 01:26:02 neilc Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol2.c,v 1.18 2005/06/12 00:00:21 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -438,8 +438,12 @@ pqParseInput2(PGconn *conn) if (pqGets(&conn->workBuffer, conn)) return; if (conn->result == NULL) + { conn->result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); + if (!conn->result) + return; + } strncpy(conn->result->cmdStatus, conn->workBuffer.data, CMDSTATUS_LEN); checkXactStatus(conn, conn->workBuffer.data); @@ -572,19 +576,18 @@ pqParseInput2(PGconn *conn) static int getRowDescriptions(PGconn *conn) { - PGresult *result; + PGresult *result = NULL; int nfields; int i; result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK); + if (!result) + goto failure; /* parseInput already read the 'T' label. */ /* the next two bytes are the number of fields */ if (pqGetInt(&(result->numAttributes), 2, conn)) - { - PQclear(result); - return EOF; - } + goto failure; nfields = result->numAttributes; /* allocate space for the attribute descriptors */ @@ -592,6 +595,8 @@ getRowDescriptions(PGconn *conn) { result->attDescs = (PGresAttDesc *) pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); + if (!result->attDescs) + goto failure; MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); } @@ -606,10 +611,7 @@ getRowDescriptions(PGconn *conn) pqGetInt(&typid, 4, conn) || pqGetInt(&typlen, 2, conn) || pqGetInt(&atttypmod, 4, conn)) - { - PQclear(result); - return EOF; - } + goto failure; /* * Since pqGetInt treats 2-byte integers as unsigned, we need to @@ -619,6 +621,8 @@ getRowDescriptions(PGconn *conn) result->attDescs[i].name = pqResultStrdup(result, conn->workBuffer.data); + if (!result->attDescs[i].name) + goto failure; result->attDescs[i].tableid = 0; result->attDescs[i].columnid = 0; result->attDescs[i].format = 0; @@ -630,6 +634,11 @@ getRowDescriptions(PGconn *conn) /* Success! */ conn->result = result; return 0; + +failure: + if (result) + PQclear(result); + return EOF; } /* @@ -685,7 +694,11 @@ getAnotherTuple(PGconn *conn, bool binary) nbytes = (nfields + BITS_PER_BYTE - 1) / BITS_PER_BYTE; /* malloc() only for unusually large field counts... */ if (nbytes > sizeof(std_bitmap)) + { bitmap = (char *) malloc(nbytes); + if (!bitmap) + goto outOfMemory; + } if (pqGetnchar(bitmap, nbytes, conn)) goto EOFexit; @@ -758,13 +771,18 @@ outOfMemory: pqClearAsyncResult(conn); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory for query result\n")); + + /* + * XXX: if PQmakeEmptyPGresult() fails, there's probably not much + * we can do to recover... + */ conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); conn->asyncStatus = PGASYNC_READY; /* Discard the failed message --- good idea? */ conn->inStart = conn->inEnd; EOFexit: - if (bitmap != std_bitmap) + if (bitmap != NULL && bitmap != std_bitmap) free(bitmap); return EOF; } @@ -780,7 +798,7 @@ EOFexit: static int pqGetErrorNotice2(PGconn *conn, bool isError) { - PGresult *res; + PGresult *res = NULL; PQExpBufferData workBuf; char *startp; char *splitp; @@ -792,10 +810,7 @@ pqGetErrorNotice2(PGconn *conn, bool isError) */ initPQExpBuffer(&workBuf); if (pqGets(&workBuf, conn)) - { - termPQExpBuffer(&workBuf); - return EOF; - } + goto failure; /* * Make a PGresult to hold the message. We temporarily lie about the @@ -803,8 +818,12 @@ pqGetErrorNotice2(PGconn *conn, bool isError) * conn->errorMessage. */ res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); + if (!res) + goto failure; res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; res->errMsg = pqResultStrdup(res, workBuf.data); + if (!res->errMsg) + goto failure; /* * Break the message into fields. We can't do very much here, but we @@ -869,6 +888,12 @@ pqGetErrorNotice2(PGconn *conn, bool isError) termPQExpBuffer(&workBuf); return 0; + +failure: + if (res) + PQclear(res); + termPQExpBuffer(&workBuf); + return EOF; } /* diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index ae8435bf1a..273159f430 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.20 2004/12/31 22:03:50 pgsql Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.21 2005/06/12 00:00:21 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -51,7 +51,7 @@ static int getParameterStatus(PGconn *conn); static int getNotify(PGconn *conn); static int getCopyStart(PGconn *conn, ExecStatusType copytype); static int getReadyForQuery(PGconn *conn); -static int build_startup_packet(const PGconn *conn, char *packet, +static int build_startup_packet(const PGconn *conn, char *packet, const PQEnvironmentOption *options); @@ -197,8 +197,12 @@ pqParseInput3(PGconn *conn) if (pqGets(&conn->workBuffer, conn)) return; if (conn->result == NULL) + { conn->result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); + if (!conn->result) + return; + } strncpy(conn->result->cmdStatus, conn->workBuffer.data, CMDSTATUS_LEN); conn->asyncStatus = PGASYNC_READY; @@ -215,8 +219,12 @@ pqParseInput3(PGconn *conn) break; case 'I': /* empty query */ if (conn->result == NULL) + { conn->result = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); + if (!conn->result) + return; + } conn->asyncStatus = PGASYNC_READY; break; case '1': /* Parse Complete */ @@ -224,8 +232,12 @@ pqParseInput3(PGconn *conn) if (conn->queryclass == PGQUERY_PREPARE) { if (conn->result == NULL) + { conn->result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); + if (!conn->result) + return; + } conn->asyncStatus = PGASYNC_READY; } break; @@ -412,14 +424,13 @@ getRowDescriptions(PGconn *conn) int i; result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK); + if (!result) + goto failure; /* parseInput already read the 'T' label and message length. */ /* the next two bytes are the number of fields */ if (pqGetInt(&(result->numAttributes), 2, conn)) - { - PQclear(result); - return EOF; - } + goto failure; nfields = result->numAttributes; /* allocate space for the attribute descriptors */ @@ -427,7 +438,9 @@ getRowDescriptions(PGconn *conn) { result->attDescs = (PGresAttDesc *) pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); - MemSet((char *) result->attDescs, 0, nfields * sizeof(PGresAttDesc)); + if (!result->attDescs) + goto failure; + MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); } /* result->binary is true only if ALL columns are binary */ @@ -451,8 +464,7 @@ getRowDescriptions(PGconn *conn) pqGetInt(&atttypmod, 4, conn) || pqGetInt(&format, 2, conn)) { - PQclear(result); - return EOF; + goto failure; } /* @@ -465,6 +477,8 @@ getRowDescriptions(PGconn *conn) result->attDescs[i].name = pqResultStrdup(result, conn->workBuffer.data); + if (!result->attDescs[i].name) + goto failure; result->attDescs[i].tableid = tableid; result->attDescs[i].columnid = columnid; result->attDescs[i].format = format; @@ -479,6 +493,10 @@ getRowDescriptions(PGconn *conn) /* Success! */ conn->result = result; return 0; + +failure: + PQclear(result); + return EOF; } /* @@ -507,7 +525,7 @@ getAnotherTuple(PGconn *conn, int msgLength) pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE); if (conn->curTuple == NULL) goto outOfMemory; - MemSet((char *) conn->curTuple, 0, nfields * sizeof(PGresAttValue)); + MemSet(conn->curTuple, 0, nfields * sizeof(PGresAttValue)); } tup = conn->curTuple; @@ -593,19 +611,11 @@ outOfMemory: int pqGetErrorNotice3(PGconn *conn, bool isError) { - PGresult *res; + PGresult *res = NULL; PQExpBufferData workBuf; char id; const char *val; - /* - * Make a PGresult to hold the accumulated fields. We temporarily lie - * about the result status, so that PQmakeEmptyPGresult doesn't - * uselessly copy conn->errorMessage. - */ - res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); - res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; - /* * Since the fields might be pretty long, we create a temporary * PQExpBuffer rather than using conn->workBuffer. workBuffer is @@ -614,6 +624,16 @@ pqGetErrorNotice3(PGconn *conn, bool isError) */ initPQExpBuffer(&workBuf); + /* + * Make a PGresult to hold the accumulated fields. We temporarily lie + * about the result status, so that PQmakeEmptyPGresult doesn't + * uselessly copy conn->errorMessage. + */ + res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); + if (!res) + goto fail; + res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; + /* * Read the fields and save into res. */ @@ -702,6 +722,8 @@ pqGetErrorNotice3(PGconn *conn, bool isError) if (isError) { res->errMsg = pqResultStrdup(res, workBuf.data); + if (!res->errMsg) + goto fail; pqClearAsyncResult(conn); conn->result = res; resetPQExpBuffer(&conn->errorMessage); @@ -825,19 +847,15 @@ getCopyStart(PGconn *conn, ExecStatusType copytype) int i; result = PQmakeEmptyPGresult(conn, copytype); + if (!result) + goto failure; if (pqGetc(&conn->copy_is_binary, conn)) - { - PQclear(result); - return EOF; - } + goto failure; result->binary = conn->copy_is_binary; /* the next two bytes are the number of fields */ if (pqGetInt(&(result->numAttributes), 2, conn)) - { - PQclear(result); - return EOF; - } + goto failure; nfields = result->numAttributes; /* allocate space for the attribute descriptors */ @@ -845,7 +863,9 @@ getCopyStart(PGconn *conn, ExecStatusType copytype) { result->attDescs = (PGresAttDesc *) pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); - MemSet((char *) result->attDescs, 0, nfields * sizeof(PGresAttDesc)); + if (!result->attDescs) + goto failure; + MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); } for (i = 0; i < nfields; i++) @@ -853,23 +873,23 @@ getCopyStart(PGconn *conn, ExecStatusType copytype) int format; if (pqGetInt(&format, 2, conn)) - { - PQclear(result); - return EOF; - } + goto failure; /* * Since pqGetInt treats 2-byte integers as unsigned, we need to * coerce these results to signed form. */ format = (int) ((int16) format); - result->attDescs[i].format = format; } /* Success! */ conn->result = result; return 0; + +failure: + PQclear(result); + return EOF; } /* diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index e4692d5d5f..6e14fa8df2 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.101 2005/06/04 20:42:43 momjian Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.102 2005/06/12 00:00:21 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -403,7 +403,7 @@ extern void pqClearAsyncResult(PGconn *conn); extern void pqSaveErrorResult(PGconn *conn); extern PGresult *pqPrepareAsyncResult(PGconn *conn); extern void -pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...) +pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt, ...) /* This lets gcc check the format string for consistency. */ __attribute__((format(printf, 2, 3))); extern int pqAddTuple(PGresult *res, PGresAttValue *tup);