Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 7b96bf4

Browse files
committed
Fix out-of-memory error handling in ParameterDescription message processing.
If libpq ran out of memory while constructing the result set, it would hang, waiting for more data from the server, which might never arrive. To fix, distinguish between out-of-memory error and not-enough-data cases, and give a proper error message back to the client on OOM. There are still similar issues in handling COPY start messages, but let's handle that as a separate patch. Michael Paquier, Amit Kapila and me. Backpatch to all supported versions.
1 parent cca705a commit 7b96bf4

File tree

1 file changed

+53
-11
lines changed

1 file changed

+53
-11
lines changed

src/interfaces/libpq/fe-protocol3.c

+53-11
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646
static void handleSyncLoss(PGconn *conn, char id, int msgLength);
4747
static int getRowDescriptions(PGconn *conn, int msgLength);
48-
static int getParamDescriptions(PGconn *conn);
48+
static int getParamDescriptions(PGconn *conn, int msgLength);
4949
static int getAnotherTuple(PGconn *conn, int msgLength);
5050
static int getParameterStatus(PGconn *conn);
5151
static int getNotify(PGconn *conn);
@@ -278,8 +278,17 @@ pqParseInput3(PGconn *conn)
278278
return;
279279
break;
280280
case 'T': /* Row Description */
281-
if (conn->result == NULL ||
282-
conn->queryclass == PGQUERY_DESCRIBE)
281+
if (conn->result != NULL &&
282+
conn->result->resultStatus == PGRES_FATAL_ERROR)
283+
{
284+
/*
285+
* We've already choked for some reason. Just discard
286+
* the data till we get to the end of the query.
287+
*/
288+
conn->inCursor += msgLength;
289+
}
290+
else if (conn->result == NULL ||
291+
conn->queryclass == PGQUERY_DESCRIBE)
283292
{
284293
/* First 'T' in a query sequence */
285294
if (getRowDescriptions(conn, msgLength))
@@ -329,9 +338,10 @@ pqParseInput3(PGconn *conn)
329338
}
330339
break;
331340
case 't': /* Parameter Description */
332-
if (getParamDescriptions(conn))
341+
if (getParamDescriptions(conn, msgLength))
333342
return;
334-
break;
343+
/* getParamDescriptions() moves inStart itself */
344+
continue;
335345
case 'D': /* Data Row */
336346
if (conn->result != NULL &&
337347
conn->result->resultStatus == PGRES_TUPLES_OK)
@@ -637,20 +647,21 @@ getRowDescriptions(PGconn *conn, int msgLength)
637647
* that shouldn't happen often, since 't' messages usually fit in a packet.
638648
*/
639649
static int
640-
getParamDescriptions(PGconn *conn)
650+
getParamDescriptions(PGconn *conn, int msgLength)
641651
{
642652
PGresult *result;
643653
int nparams;
644654
int i;
655+
const char *errmsg = NULL;
645656

646657
result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK);
647658
if (!result)
648-
goto failure;
659+
goto advance_and_error;
649660

650661
/* parseInput already read the 't' label and message length. */
651662
/* the next two bytes are the number of parameters */
652663
if (pqGetInt(&(result->numParameters), 2, conn))
653-
goto failure;
664+
goto not_enough_data;
654665
nparams = result->numParameters;
655666

656667
/* allocate space for the parameter descriptors */
@@ -659,7 +670,7 @@ getParamDescriptions(PGconn *conn)
659670
result->paramDescs = (PGresParamDesc *)
660671
pqResultAlloc(result, nparams * sizeof(PGresParamDesc), TRUE);
661672
if (!result->paramDescs)
662-
goto failure;
673+
goto advance_and_error;
663674
MemSet(result->paramDescs, 0, nparams * sizeof(PGresParamDesc));
664675
}
665676

@@ -669,17 +680,48 @@ getParamDescriptions(PGconn *conn)
669680
int typid;
670681

671682
if (pqGetInt(&typid, 4, conn))
672-
goto failure;
683+
goto not_enough_data;
673684
result->paramDescs[i].typid = typid;
674685
}
675686

676687
/* Success! */
677688
conn->result = result;
689+
690+
/* Advance inStart to show that the "t" message has been processed. */
691+
conn->inStart = conn->inCursor;
692+
678693
return 0;
679694

680-
failure:
695+
not_enough_data:
681696
PQclear(result);
682697
return EOF;
698+
699+
advance_and_error:
700+
/* Discard unsaved result, if any */
701+
if (result && result != conn->result)
702+
PQclear(result);
703+
704+
/* Discard the failed message by pretending we read it */
705+
conn->inStart += 5 + msgLength;
706+
707+
/*
708+
* Replace partially constructed result with an error result. First
709+
* discard the old result to try to win back some memory.
710+
*/
711+
pqClearAsyncResult(conn);
712+
713+
/*
714+
* If preceding code didn't provide an error message, assume "out of
715+
* memory" was meant. The advantage of having this special case is that
716+
* freeing the old result first greatly improves the odds that gettext()
717+
* will succeed in providing a translation.
718+
*/
719+
if (!errmsg)
720+
errmsg = libpq_gettext("out of memory");
721+
printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
722+
pqSaveErrorResult(conn);
723+
724+
return 0;
683725
}
684726

685727
/*

0 commit comments

Comments
 (0)