Skip to content

Commit 51c54bb

Browse files
committed
Re-simplify management of inStart in pqParseInput3's subroutines.
Commit 92785da copied some logic related to advancement of inStart from pqParseInput3 into getRowDescriptions and getAnotherTuple, because it wanted to allow user-defined row processor callbacks to potentially longjmp out of the library, and inStart would have to be updated before that happened to avoid an infinite loop. We later decided that that API was impossibly fragile and reverted it, but we didn't undo all of the related code changes, and this bit of messiness survived. Undo it now so that there's just one place in pqParseInput3's processing where inStart is advanced; this will simplify addition of better tracing support. getParamDescriptions had grown similar processing somewhere along the way (not in 92785da; I didn't track down just when), but it's actually buggy because its handling of corrupt-message cases seems to have been copied from the v2 logic where we lacked a known message length. The cases where we "goto not_enough_data" should not simply return EOF, because then we won't consume the message, potentially creating an infinite loop. That situation now represents a definitively corrupt message, and we should report it as such. Although no field reports of getParamDescriptions getting stuck in a loop have been seen, it seems appropriate to back-patch that fix. I chose to back-patch all of this to keep the logic looking more alike in supported branches. Discussion: https://postgr.es/m/2217283.1615411989@sss.pgh.pa.us
1 parent f71519e commit 51c54bb

File tree

1 file changed

+23
-57
lines changed

1 file changed

+23
-57
lines changed

src/interfaces/libpq/fe-protocol3.c

Lines changed: 23 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,6 @@ pqParseInput3(PGconn *conn)
290290
/* First 'T' in a query sequence */
291291
if (getRowDescriptions(conn, msgLength))
292292
return;
293-
/* getRowDescriptions() moves inStart itself */
294-
continue;
295293
}
296294
else
297295
{
@@ -337,17 +335,14 @@ pqParseInput3(PGconn *conn)
337335
case 't': /* Parameter Description */
338336
if (getParamDescriptions(conn, msgLength))
339337
return;
340-
/* getParamDescriptions() moves inStart itself */
341-
continue;
338+
break;
342339
case 'D': /* Data Row */
343340
if (conn->result != NULL &&
344341
conn->result->resultStatus == PGRES_TUPLES_OK)
345342
{
346343
/* Read another tuple of a normal query response */
347344
if (getAnotherTuple(conn, msgLength))
348345
return;
349-
/* getAnotherTuple() moves inStart itself */
350-
continue;
351346
}
352347
else if (conn->result != NULL &&
353348
conn->result->resultStatus == PGRES_FATAL_ERROR)
@@ -462,7 +457,6 @@ handleSyncLoss(PGconn *conn, char id, int msgLength)
462457
* command for a prepared statement) containing the attribute data.
463458
* Returns: 0 if processed message successfully, EOF to suspend parsing
464459
* (the latter case is not actually used currently).
465-
* In the former case, conn->inStart has been advanced past the message.
466460
*/
467461
static int
468462
getRowDescriptions(PGconn *conn, int msgLength)
@@ -567,19 +561,9 @@ getRowDescriptions(PGconn *conn, int msgLength)
567561
result->binary = 0;
568562
}
569563

570-
/* Sanity check that we absorbed all the data */
571-
if (conn->inCursor != conn->inStart + 5 + msgLength)
572-
{
573-
errmsg = libpq_gettext("extraneous data in \"T\" message");
574-
goto advance_and_error;
575-
}
576-
577564
/* Success! */
578565
conn->result = result;
579566

580-
/* Advance inStart to show that the "T" message has been processed. */
581-
conn->inStart = conn->inCursor;
582-
583567
/*
584568
* If we're doing a Describe, we're done, and ready to pass the result
585569
* back to the client.
@@ -603,9 +587,6 @@ getRowDescriptions(PGconn *conn, int msgLength)
603587
if (result && result != conn->result)
604588
PQclear(result);
605589

606-
/* Discard the failed message by pretending we read it */
607-
conn->inStart += 5 + msgLength;
608-
609590
/*
610591
* Replace partially constructed result with an error result. First
611592
* discard the old result to try to win back some memory.
@@ -624,6 +605,12 @@ getRowDescriptions(PGconn *conn, int msgLength)
624605
appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
625606
pqSaveErrorResult(conn);
626607

608+
/*
609+
* Show the message as fully consumed, else pqParseInput3 will overwrite
610+
* our error with a complaint about that.
611+
*/
612+
conn->inCursor = conn->inStart + 5 + msgLength;
613+
627614
/*
628615
* Return zero to allow input parsing to continue. Subsequent "D"
629616
* messages will be ignored until we get to end of data, since an error
@@ -635,12 +622,8 @@ getRowDescriptions(PGconn *conn, int msgLength)
635622
/*
636623
* parseInput subroutine to read a 't' (ParameterDescription) message.
637624
* We'll build a new PGresult structure containing the parameter data.
638-
* Returns: 0 if completed message, EOF if not enough data yet.
639-
* In the former case, conn->inStart has been advanced past the message.
640-
*
641-
* Note that if we run out of data, we have to release the partially
642-
* constructed PGresult, and rebuild it again next time. Fortunately,
643-
* that shouldn't happen often, since 't' messages usually fit in a packet.
625+
* Returns: 0 if processed message successfully, EOF to suspend parsing
626+
* (the latter case is not actually used currently).
644627
*/
645628
static int
646629
getParamDescriptions(PGconn *conn, int msgLength)
@@ -680,33 +663,19 @@ getParamDescriptions(PGconn *conn, int msgLength)
680663
result->paramDescs[i].typid = typid;
681664
}
682665

683-
/* Sanity check that we absorbed all the data */
684-
if (conn->inCursor != conn->inStart + 5 + msgLength)
685-
{
686-
errmsg = libpq_gettext("extraneous data in \"t\" message");
687-
goto advance_and_error;
688-
}
689-
690666
/* Success! */
691667
conn->result = result;
692668

693-
/* Advance inStart to show that the "t" message has been processed. */
694-
conn->inStart = conn->inCursor;
695-
696669
return 0;
697670

698671
not_enough_data:
699-
PQclear(result);
700-
return EOF;
672+
errmsg = libpq_gettext("insufficient data in \"t\" message");
701673

702674
advance_and_error:
703675
/* Discard unsaved result, if any */
704676
if (result && result != conn->result)
705677
PQclear(result);
706678

707-
/* Discard the failed message by pretending we read it */
708-
conn->inStart += 5 + msgLength;
709-
710679
/*
711680
* Replace partially constructed result with an error result. First
712681
* discard the old result to try to win back some memory.
@@ -724,6 +693,12 @@ getParamDescriptions(PGconn *conn, int msgLength)
724693
appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
725694
pqSaveErrorResult(conn);
726695

696+
/*
697+
* Show the message as fully consumed, else pqParseInput3 will overwrite
698+
* our error with a complaint about that.
699+
*/
700+
conn->inCursor = conn->inStart + 5 + msgLength;
701+
727702
/*
728703
* Return zero to allow input parsing to continue. Essentially, we've
729704
* replaced the COMMAND_OK result with an error result, but since this
@@ -737,7 +712,6 @@ getParamDescriptions(PGconn *conn, int msgLength)
737712
* We fill rowbuf with column pointers and then call the row processor.
738713
* Returns: 0 if processed message successfully, EOF to suspend parsing
739714
* (the latter case is not actually used currently).
740-
* In the former case, conn->inStart has been advanced past the message.
741715
*/
742716
static int
743717
getAnotherTuple(PGconn *conn, int msgLength)
@@ -810,28 +784,14 @@ getAnotherTuple(PGconn *conn, int msgLength)
810784
}
811785
}
812786

813-
/* Sanity check that we absorbed all the data */
814-
if (conn->inCursor != conn->inStart + 5 + msgLength)
815-
{
816-
errmsg = libpq_gettext("extraneous data in \"D\" message");
817-
goto advance_and_error;
818-
}
819-
820-
/* Advance inStart to show that the "D" message has been processed. */
821-
conn->inStart = conn->inCursor;
822-
823787
/* Process the collected row */
824788
errmsg = NULL;
825789
if (pqRowProcessor(conn, &errmsg))
826790
return 0; /* normal, successful exit */
827791

828-
goto set_error_result; /* pqRowProcessor failed, report it */
792+
/* pqRowProcessor failed, fall through to report it */
829793

830794
advance_and_error:
831-
/* Discard the failed message by pretending we read it */
832-
conn->inStart += 5 + msgLength;
833-
834-
set_error_result:
835795

836796
/*
837797
* Replace partially constructed result with an error result. First
@@ -851,6 +811,12 @@ getAnotherTuple(PGconn *conn, int msgLength)
851811
appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
852812
pqSaveErrorResult(conn);
853813

814+
/*
815+
* Show the message as fully consumed, else pqParseInput3 will overwrite
816+
* our error with a complaint about that.
817+
*/
818+
conn->inCursor = conn->inStart + 5 + msgLength;
819+
854820
/*
855821
* Return zero to allow input parsing to continue. Subsequent "D"
856822
* messages will be ignored until we get to end of data, since an error

0 commit comments

Comments
 (0)