diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index e1e2d18e3a4..a97e73cf99d 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -16,6 +16,7 @@ #include #include +#include #include "libpq-fe.h" #include "libpq-int.h" @@ -51,7 +52,8 @@ static bool static_std_strings = false; static PGEvent *dupEvents(PGEvent *events, int count); -static bool pqAddTuple(PGresult *res, PGresAttValue *tup); +static bool pqAddTuple(PGresult *res, PGresAttValue *tup, + const char **errmsgp); static bool PQsendQueryStart(PGconn *conn); static int PQsendQueryGuts(PGconn *conn, const char *command, @@ -416,18 +418,26 @@ dupEvents(PGEvent *events, int count) * equal to PQntuples(res). If it is equal, a new tuple is created and * added to the result. * Returns a non-zero value for success and zero for failure. + * (On failure, we report the specific problem via pqInternalNotice.) */ int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len) { PGresAttValue *attval; + const char *errmsg = NULL; + /* Note that this check also protects us against null "res" */ if (!check_field_number(res, field_num)) return FALSE; /* Invalid tup_num, must be <= ntups */ if (tup_num < 0 || tup_num > res->ntups) + { + pqInternalNotice(&res->noticeHooks, + "row number %d is out of range 0..%d", + tup_num, res->ntups); return FALSE; + } /* need to allocate a new tuple? */ if (tup_num == res->ntups) @@ -440,7 +450,7 @@ PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len) TRUE); if (!tup) - return FALSE; + goto fail; /* initialize each column to NULL */ for (i = 0; i < res->numAttributes; i++) @@ -450,8 +460,8 @@ PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len) } /* add it to the array */ - if (!pqAddTuple(res, tup)) - return FALSE; + if (!pqAddTuple(res, tup, &errmsg)) + goto fail; } attval = &res->tuples[tup_num][field_num]; @@ -471,13 +481,24 @@ PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len) { attval->value = (char *) pqResultAlloc(res, len + 1, TRUE); if (!attval->value) - return FALSE; + goto fail; attval->len = len; memcpy(attval->value, value, len); attval->value[len] = '\0'; } return TRUE; + + /* + * Report failure via pqInternalNotice. If preceding code didn't provide + * an error message, assume "out of memory" was meant. + */ +fail: + if (!errmsg) + errmsg = libpq_gettext("out of memory"); + pqInternalNotice(&res->noticeHooks, "%s", errmsg); + + return FALSE; } /* @@ -847,10 +868,13 @@ pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...) /* * pqAddTuple * add a row pointer to the PGresult structure, growing it if necessary - * Returns TRUE if OK, FALSE if not enough memory to add the row + * Returns TRUE if OK, FALSE if an error prevented adding the row + * + * On error, *errmsgp can be set to an error string to be returned. + * If it is left NULL, the error is presumed to be "out of memory". */ static bool -pqAddTuple(PGresult *res, PGresAttValue *tup) +pqAddTuple(PGresult *res, PGresAttValue *tup, const char **errmsgp) { if (res->ntups >= res->tupArrSize) { @@ -865,9 +889,36 @@ pqAddTuple(PGresult *res, PGresAttValue *tup) * existing allocation. Note that the positions beyond res->ntups are * garbage, not necessarily NULL. */ - int newSize = (res->tupArrSize > 0) ? res->tupArrSize * 2 : 128; + int newSize; PGresAttValue **newTuples; + /* + * Since we use integers for row numbers, we can't support more than + * INT_MAX rows. Make sure we allow that many, though. + */ + if (res->tupArrSize <= INT_MAX / 2) + newSize = (res->tupArrSize > 0) ? res->tupArrSize * 2 : 128; + else if (res->tupArrSize < INT_MAX) + newSize = INT_MAX; + else + { + *errmsgp = libpq_gettext("PGresult cannot support more than INT_MAX tuples"); + return FALSE; + } + + /* + * Also, on 32-bit platforms we could, in theory, overflow size_t even + * before newSize gets to INT_MAX. (In practice we'd doubtless hit + * OOM long before that, but let's check.) + */ +#if INT_MAX >= (SIZE_MAX / 2) + if (newSize > SIZE_MAX / sizeof(PGresAttValue *)) + { + *errmsgp = libpq_gettext("size_t overflow"); + return FALSE; + } +#endif + if (res->tuples == NULL) newTuples = (PGresAttValue **) malloc(newSize * sizeof(PGresAttValue *)); @@ -1093,7 +1144,7 @@ pqRowProcessor(PGconn *conn, const char **errmsgp) } /* And add the tuple to the PGresult's tuple array */ - if (!pqAddTuple(res, tup)) + if (!pqAddTuple(res, tup, errmsgp)) goto fail; /*