From 64d7ae9b2966f3aa57dc3359d9047436c49fb10f Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Sun, 11 Oct 2015 21:57:27 +0900
Subject: [PATCH 2/2] Fix OOM error handling in BIND protocol of libpq

---
 src/interfaces/libpq/fe-protocol3.c | 67 +++++++++++++++++++++++++++++++------
 1 file changed, 56 insertions(+), 11 deletions(-)

diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 1079911..a8c672a 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);
@@ -280,8 +280,17 @@ pqParseInput3(PGconn *conn)
 						return;
 					break;
 				case 'T':		/* Row Description */
-					if (conn->result == NULL ||
-						conn->queryclass == PGQUERY_DESCRIBE)
+					if (conn->result != NULL &&
+						conn->result->resultStatus == PGRES_FATAL_ERROR)
+					{
+						/*
+						 * We've already choked for some reason.  Just discard
+						 * the data till we get to the end of the query.
+						 */
+						conn->inCursor += msgLength;
+					}
+					else if (conn->result == NULL ||
+							 conn->queryclass == PGQUERY_DESCRIBE)
 					{
 						/* First 'T' in a query sequence */
 						if (getRowDescriptions(conn, msgLength))
@@ -331,9 +340,10 @@ pqParseInput3(PGconn *conn)
 					}
 					break;
 				case 't':		/* Parameter Description */
-					if (getParamDescriptions(conn))
+					if (getParamDescriptions(conn, msgLength))
 						return;
-					break;
+					/* getParamDescriptions() moves inStart itself */
+					continue;
 				case 'D':		/* Data Row */
 					if (conn->result != NULL &&
 						conn->result->resultStatus == PGRES_TUPLES_OK)
@@ -642,20 +652,21 @@ advance_and_error:
  * 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 = NULL;
 
 	result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK);
 	if (!result)
-		goto failure;
+		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;
+		goto not_enough_data;
 	nparams = result->numParameters;
 
 	/* allocate space for the parameter descriptors */
@@ -664,7 +675,7 @@ getParamDescriptions(PGconn *conn)
 		result->paramDescs = (PGresParamDesc *)
 			pqResultAlloc(result, nparams * sizeof(PGresParamDesc), TRUE);
 		if (!result->paramDescs)
-			goto failure;
+			goto advance_and_error;
 		MemSet(result->paramDescs, 0, nparams * sizeof(PGresParamDesc));
 	}
 
@@ -674,17 +685,51 @@ getParamDescriptions(PGconn *conn)
 		int			typid;
 
 		if (pqGetInt(&typid, 4, conn))
-			goto failure;
+			goto not_enough_data;
 		result->paramDescs[i].typid = typid;
 	}
 
 	/* Success! */
 	conn->result = result;
+
+	/*
+	 * Advance inStart to show that the copy related message has been
+	 * processed.
+	 */
+	conn->inStart = conn->inCursor;
+
 	return 0;
 
-failure:
+not_enough_data:
 	PQclear(result);
 	return EOF;
+
+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->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 0;
 }
 
 /*
-- 
2.6.0

