]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Improve handling of out-of-memory in libpq.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Tue, 7 Jul 2015 15:37:45 +0000 (18:37 +0300)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Tue, 7 Jul 2015 15:45:22 +0000 (18:45 +0300)
If an allocation fails in the main message handling loop, pqParseInput3
or pqParseInput2, it should not be treated as "not enough data available
yet". Otherwise libpq will wait indefinitely for more data to arrive from
the server, and gets stuck forever.

This isn't a complete fix - getParamDescriptions and getCopyStart still
have the same issue, but it's a step in the right direction.

Michael Paquier and me. Backpatch to all supported versions.

src/interfaces/libpq/fe-protocol2.c
src/interfaces/libpq/fe-protocol3.c

index 8f60f6ccc0eaaf1c240b98c9513f7f87fbd542a4..cf765bee4fa85343d12c34b389de9b2673dfa8f2 100644 (file)
@@ -498,10 +498,17 @@ pqParseInput2(PGconn *conn)
                                                conn->result = PQmakeEmptyPGresult(conn,
                                                                                                                   PGRES_COMMAND_OK);
                                                if (!conn->result)
-                                                       return;
+                                               {
+                                                       printfPQExpBuffer(&conn->errorMessage,
+                                                                                         libpq_gettext("out of memory"));
+                                                       pqSaveErrorResult(conn);
+                                               }
+                                       }
+                                       if (conn->result)
+                                       {
+                                               strlcpy(conn->result->cmdStatus, conn->workBuffer.data,
+                                                               CMDSTATUS_LEN);
                                        }
-                                       strlcpy(conn->result->cmdStatus, conn->workBuffer.data,
-                                                       CMDSTATUS_LEN);
                                        checkXactStatus(conn, conn->workBuffer.data);
                                        conn->asyncStatus = PGASYNC_READY;
                                        break;
@@ -522,8 +529,16 @@ pqParseInput2(PGconn *conn)
                                                                                 "unexpected character %c following empty query response (\"I\" message)",
                                                                                 id);
                                        if (conn->result == NULL)
+                                       {
                                                conn->result = PQmakeEmptyPGresult(conn,
                                                                                                                   PGRES_EMPTY_QUERY);
+                                               if (!conn->result)
+                                               {
+                                                       printfPQExpBuffer(&conn->errorMessage,
+                                                                                         libpq_gettext("out of memory"));
+                                                       pqSaveErrorResult(conn);
+                                               }
+                                       }
                                        conn->asyncStatus = PGASYNC_READY;
                                        break;
                                case 'K':               /* secret key data from the backend */
@@ -965,14 +980,17 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
         * Make a PGresult to hold the message.  We temporarily lie about the
         * result status, so that PQmakeEmptyPGresult doesn't uselessly copy
         * conn->errorMessage.
+        *
+        * NB: This allocation can fail, if you run out of memory. The rest of the
+        * function handles that gracefully, and we still try to set the error
+        * message as the connection's error message.
         */
        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;
+       if (res)
+       {
+               res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR;
+               res->errMsg = pqResultStrdup(res, workBuf.data);
+       }
 
        /*
         * Break the message into fields.  We can't do very much here, but we can
@@ -1024,15 +1042,22 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
                pqClearAsyncResult(conn);
                conn->result = res;
                resetPQExpBuffer(&conn->errorMessage);
-               appendPQExpBufferStr(&conn->errorMessage, res->errMsg);
+               if (res && !PQExpBufferDataBroken(workBuf) && res->errMsg)
+                       appendPQExpBufferStr(&conn->errorMessage, res->errMsg);
+               else
+                       printfPQExpBuffer(&conn->errorMessage,
+                                                         libpq_gettext("out of memory"));
                if (conn->xactStatus == PQTRANS_INTRANS)
                        conn->xactStatus = PQTRANS_INERROR;
        }
        else
        {
-               if (res->noticeHooks.noticeRec != NULL)
-                       (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
-               PQclear(res);
+               if (res)
+               {
+                       if (res->noticeHooks.noticeRec != NULL)
+                               (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
+                       PQclear(res);
+               }
        }
 
        termPQExpBuffer(&workBuf);
index d03962afeb62787b2e26a901e5da8d36c941ded5..290f93c6ab10038feb59b59437fb9866315cd111 100644 (file)
@@ -204,10 +204,15 @@ pqParseInput3(PGconn *conn)
                                                conn->result = PQmakeEmptyPGresult(conn,
                                                                                                                   PGRES_COMMAND_OK);
                                                if (!conn->result)
-                                                       return;
+                                               {
+                                                       printfPQExpBuffer(&conn->errorMessage,
+                                                                                         libpq_gettext("out of memory"));
+                                                       pqSaveErrorResult(conn);
+                                               }
                                        }
-                                       strlcpy(conn->result->cmdStatus, conn->workBuffer.data,
-                                                       CMDSTATUS_LEN);
+                                       if (conn->result)
+                                               strlcpy(conn->result->cmdStatus, conn->workBuffer.data,
+                                                               CMDSTATUS_LEN);
                                        conn->asyncStatus = PGASYNC_READY;
                                        break;
                                case 'E':               /* error return */
@@ -226,7 +231,11 @@ pqParseInput3(PGconn *conn)
                                                conn->result = PQmakeEmptyPGresult(conn,
                                                                                                                   PGRES_EMPTY_QUERY);
                                                if (!conn->result)
-                                                       return;
+                                               {
+                                                       printfPQExpBuffer(&conn->errorMessage,
+                                                                                         libpq_gettext("out of memory"));
+                                                       pqSaveErrorResult(conn);
+                                               }
                                        }
                                        conn->asyncStatus = PGASYNC_READY;
                                        break;
@@ -239,7 +248,11 @@ pqParseInput3(PGconn *conn)
                                                        conn->result = PQmakeEmptyPGresult(conn,
                                                                                                                   PGRES_COMMAND_OK);
                                                        if (!conn->result)
-                                                               return;
+                                                       {
+                                                               printfPQExpBuffer(&conn->errorMessage,
+                                                                                                 libpq_gettext("out of memory"));
+                                                               pqSaveErrorResult(conn);
+                                                       }
                                                }
                                                conn->asyncStatus = PGASYNC_READY;
                                        }
@@ -306,7 +319,11 @@ pqParseInput3(PGconn *conn)
                                                        conn->result = PQmakeEmptyPGresult(conn,
                                                                                                                   PGRES_COMMAND_OK);
                                                        if (!conn->result)
-                                                               return;
+                                                       {
+                                                               printfPQExpBuffer(&conn->errorMessage,
+                                                                                                 libpq_gettext("out of memory"));
+                                                               pqSaveErrorResult(conn);
+                                                       }
                                                }
                                                conn->asyncStatus = PGASYNC_READY;
                                        }
@@ -822,11 +839,14 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
         * Make a PGresult to hold the accumulated fields.  We temporarily lie
         * about the result status, so that PQmakeEmptyPGresult doesn't uselessly
         * copy conn->errorMessage.
+        *
+        * NB: This allocation can fail, if you run out of memory. The rest of the
+        * function handles that gracefully, and we still try to set the error
+        * message as the connection's error message.
         */
        res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
-       if (!res)
-               goto fail;
-       res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR;
+       if (res)
+               res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR;
 
        /*
         * Read the fields and save into res.
@@ -966,20 +986,27 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
         */
        if (isError)
        {
-               res->errMsg = pqResultStrdup(res, workBuf.data);
-               if (!res->errMsg)
-                       goto fail;
+               if (res)
+                       res->errMsg = pqResultStrdup(res, workBuf.data);
                pqClearAsyncResult(conn);
                conn->result = res;
-               appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
+               if (PQExpBufferDataBroken(workBuf))
+                       printfPQExpBuffer(&conn->errorMessage,
+                                                         libpq_gettext("out of memory"));
+               else
+                       appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
        }
        else
        {
-               /* We can cheat a little here and not copy the message. */
-               res->errMsg = workBuf.data;
-               if (res->noticeHooks.noticeRec != NULL)
-                       (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
-               PQclear(res);
+               /* if we couldn't allocate the result set, just discard the NOTICE */
+               if (res)
+               {
+                       /* We can cheat a little here and not copy the message. */
+                       res->errMsg = workBuf.data;
+                       if (res->noticeHooks.noticeRec != NULL)
+                               (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
+                       PQclear(res);
+               }
        }
 
        termPQExpBuffer(&workBuf);