From ba65eb6cd8dbd74d2d33a89d372982e4a940ca6c Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Thu, 9 Jul 2015 13:28:31 +0000
Subject: [PATCH 2/2] Prevent hangling of libpq for BIND message results on OOM

When failing on OOM, pqParseInput3, could finish in an infinite loop
state for this message. This commit improves the OOM error handling
of getParamDescriptions() to prevent that by using the new aaync flag
PGAYNC_FATAL.
---
 src/interfaces/libpq/fe-protocol3.c | 61 ++++++++++++++++++++++++++++++-------
 1 file changed, 50 insertions(+), 11 deletions(-)

diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index d0b22b7..f71cbe5 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -45,7 +45,7 @@
 
 static void handleSyncLoss(PGconn *conn, char id, int msgLength);
 static int	getRowDescriptions(PGconn *conn, int msgLength);
-static int	getParamDescriptions(PGconn *conn);
+static int	getParamDescriptions(PGconn *conn, int msgLength);
 static int	getAnotherTuple(PGconn *conn, int msgLength);
 static int	getParameterStatus(PGconn *conn);
 static int	getNotify(PGconn *conn);
@@ -331,8 +331,11 @@ pqParseInput3(PGconn *conn)
 					}
 					break;
 				case 't':		/* Parameter Description */
-					if (getParamDescriptions(conn))
+					if (getParamDescriptions(conn, msgLength))
+					{
+						conn->asyncStatus = PGASYNC_FATAL;
 						return;
+					}
 					break;
 				case 'D':		/* Data Row */
 					if (conn->result != NULL &&
@@ -641,27 +644,34 @@ advance_and_error:
 /*
  * parseInput subroutine to read a 't' (ParameterDescription) message.
  * We'll build a new PGresult structure containing the parameter data.
- * Returns: 0 if completed message, EOF if not enough data yet.
+ * Returns: 0 if completed message, EOF in case of error.
  *
  * Note that if we run out of data, we have to release the partially
  * constructed PGresult, and rebuild it again next time.  Fortunately,
  * that shouldn't happen often, since 't' messages usually fit in a packet.
  */
 static int
-getParamDescriptions(PGconn *conn)
+getParamDescriptions(PGconn *conn, int msgLength)
 {
 	PGresult   *result;
 	int			nparams;
 	int			i;
+	const char *errmsg;
 
 	result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK);
 	if (!result)
-		goto failure;
+	{
+		errmsg = NULL;
+		goto advance_and_error;
+	}
 
 	/* parseInput already read the 't' label and message length. */
 	/* the next two bytes are the number of parameters */
-	if (pqGetInt(&(result->numParameters), 2, conn))
-		goto failure;
+	if (pqGetInt(&result->numParameters, 2, conn))
+	{
+		errmsg = libpq_gettext("extraneous data in \"t\" message");
+		goto advance_and_error;
+	}
 	nparams = result->numParameters;
 
 	/* allocate space for the parameter descriptors */
@@ -670,7 +680,10 @@ getParamDescriptions(PGconn *conn)
 		result->paramDescs = (PGresParamDesc *)
 			pqResultAlloc(result, nparams * sizeof(PGresParamDesc), TRUE);
 		if (!result->paramDescs)
-			goto failure;
+		{
+			errmsg = NULL;
+			goto advance_and_error;
+		}
 		MemSet(result->paramDescs, 0, nparams * sizeof(PGresParamDesc));
 	}
 
@@ -680,7 +693,10 @@ getParamDescriptions(PGconn *conn)
 		int			typid;
 
 		if (pqGetInt(&typid, 4, conn))
-			goto failure;
+		{
+			errmsg = libpq_gettext("extraneous data in \"t\" message");
+			goto advance_and_error;
+		}
 		result->paramDescs[i].typid = typid;
 	}
 
@@ -688,8 +704,31 @@ getParamDescriptions(PGconn *conn)
 	conn->result = result;
 	return 0;
 
-failure:
-	PQclear(result);
+advance_and_error:
+	/* Discard unsaved result, if any */
+	if (result && result != conn->result)
+		PQclear(result);
+
+	/* Discard the failed message by pretending we read it */
+	conn->inCursor = conn->inStart + 5 + msgLength;
+
+	/*
+	 * Replace partially constructed result with an error result. First
+	 * discard the old result to try to win back some memory.
+	 */
+	pqClearAsyncResult(conn);
+
+	/*
+	 * If preceding code didn't provide an error message, assume "out of
+	 * memory" was meant.  The advantage of having this special case is that
+	 * freeing the old result first greatly improves the odds that gettext()
+	 * will succeed in providing a translation.
+	 */
+	if (!errmsg)
+		errmsg = libpq_gettext("out of memory");
+
+	printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
+	pqSaveErrorResult(conn);
 	return EOF;
 }
 
-- 
2.4.5

