Skip to content

Commit a98e53e

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 b6b1d72 commit a98e53e

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
@@ -293,8 +293,6 @@ pqParseInput3(PGconn *conn)
293293
/* First 'T' in a query sequence */
294294
if (getRowDescriptions(conn, msgLength))
295295
return;
296-
/* getRowDescriptions() moves inStart itself */
297-
continue;
298296
}
299297
else
300298
{
@@ -340,17 +338,14 @@ pqParseInput3(PGconn *conn)
340338
case 't': /* Parameter Description */
341339
if (getParamDescriptions(conn, msgLength))
342340
return;
343-
/* getParamDescriptions() moves inStart itself */
344-
continue;
341+
break;
345342
case 'D': /* Data Row */
346343
if (conn->result != NULL &&
347344
conn->result->resultStatus == PGRES_TUPLES_OK)
348345
{
349346
/* Read another tuple of a normal query response */
350347
if (getAnotherTuple(conn, msgLength))
351348
return;
352-
/* getAnotherTuple() moves inStart itself */
353-
continue;
354349
}
355350
else if (conn->result != NULL &&
356351
conn->result->resultStatus == PGRES_FATAL_ERROR)
@@ -467,7 +462,6 @@ handleSyncLoss(PGconn *conn, char id, int msgLength)
467462
* command for a prepared statement) containing the attribute data.
468463
* Returns: 0 if processed message successfully, EOF to suspend parsing
469464
* (the latter case is not actually used currently).
470-
* In the former case, conn->inStart has been advanced past the message.
471465
*/
472466
static int
473467
getRowDescriptions(PGconn *conn, int msgLength)
@@ -572,19 +566,9 @@ getRowDescriptions(PGconn *conn, int msgLength)
572566
result->binary = 0;
573567
}
574568

575-
/* Sanity check that we absorbed all the data */
576-
if (conn->inCursor != conn->inStart + 5 + msgLength)
577-
{
578-
errmsg = libpq_gettext("extraneous data in \"T\" message");
579-
goto advance_and_error;
580-
}
581-
582569
/* Success! */
583570
conn->result = result;
584571

585-
/* Advance inStart to show that the "T" message has been processed. */
586-
conn->inStart = conn->inCursor;
587-
588572
/*
589573
* If we're doing a Describe, we're done, and ready to pass the result
590574
* back to the client.
@@ -608,9 +592,6 @@ getRowDescriptions(PGconn *conn, int msgLength)
608592
if (result && result != conn->result)
609593
PQclear(result);
610594

611-
/* Discard the failed message by pretending we read it */
612-
conn->inStart += 5 + msgLength;
613-
614595
/*
615596
* Replace partially constructed result with an error result. First
616597
* discard the old result to try to win back some memory.
@@ -629,6 +610,12 @@ getRowDescriptions(PGconn *conn, int msgLength)
629610
printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
630611
pqSaveErrorResult(conn);
631612

613+
/*
614+
* Show the message as fully consumed, else pqParseInput3 will overwrite
615+
* our error with a complaint about that.
616+
*/
617+
conn->inCursor = conn->inStart + 5 + msgLength;
618+
632619
/*
633620
* Return zero to allow input parsing to continue. Subsequent "D"
634621
* messages will be ignored until we get to end of data, since an error
@@ -640,12 +627,8 @@ getRowDescriptions(PGconn *conn, int msgLength)
640627
/*
641628
* parseInput subroutine to read a 't' (ParameterDescription) message.
642629
* We'll build a new PGresult structure containing the parameter data.
643-
* Returns: 0 if completed message, EOF if not enough data yet.
644-
* In the former case, conn->inStart has been advanced past the message.
645-
*
646-
* Note that if we run out of data, we have to release the partially
647-
* constructed PGresult, and rebuild it again next time. Fortunately,
648-
* that shouldn't happen often, since 't' messages usually fit in a packet.
630+
* Returns: 0 if processed message successfully, EOF to suspend parsing
631+
* (the latter case is not actually used currently).
649632
*/
650633
static int
651634
getParamDescriptions(PGconn *conn, int msgLength)
@@ -685,33 +668,19 @@ getParamDescriptions(PGconn *conn, int msgLength)
685668
result->paramDescs[i].typid = typid;
686669
}
687670

688-
/* Sanity check that we absorbed all the data */
689-
if (conn->inCursor != conn->inStart + 5 + msgLength)
690-
{
691-
errmsg = libpq_gettext("extraneous data in \"t\" message");
692-
goto advance_and_error;
693-
}
694-
695671
/* Success! */
696672
conn->result = result;
697673

698-
/* Advance inStart to show that the "t" message has been processed. */
699-
conn->inStart = conn->inCursor;
700-
701674
return 0;
702675

703676
not_enough_data:
704-
PQclear(result);
705-
return EOF;
677+
errmsg = libpq_gettext("insufficient data in \"t\" message");
706678

707679
advance_and_error:
708680
/* Discard unsaved result, if any */
709681
if (result && result != conn->result)
710682
PQclear(result);
711683

712-
/* Discard the failed message by pretending we read it */
713-
conn->inStart += 5 + msgLength;
714-
715684
/*
716685
* Replace partially constructed result with an error result. First
717686
* discard the old result to try to win back some memory.
@@ -729,6 +698,12 @@ getParamDescriptions(PGconn *conn, int msgLength)
729698
printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
730699
pqSaveErrorResult(conn);
731700

701+
/*
702+
* Show the message as fully consumed, else pqParseInput3 will overwrite
703+
* our error with a complaint about that.
704+
*/
705+
conn->inCursor = conn->inStart + 5 + msgLength;
706+
732707
/*
733708
* Return zero to allow input parsing to continue. Essentially, we've
734709
* replaced the COMMAND_OK result with an error result, but since this
@@ -742,7 +717,6 @@ getParamDescriptions(PGconn *conn, int msgLength)
742717
* We fill rowbuf with column pointers and then call the row processor.
743718
* Returns: 0 if processed message successfully, EOF to suspend parsing
744719
* (the latter case is not actually used currently).
745-
* In the former case, conn->inStart has been advanced past the message.
746720
*/
747721
static int
748722
getAnotherTuple(PGconn *conn, int msgLength)
@@ -815,28 +789,14 @@ getAnotherTuple(PGconn *conn, int msgLength)
815789
}
816790
}
817791

818-
/* Sanity check that we absorbed all the data */
819-
if (conn->inCursor != conn->inStart + 5 + msgLength)
820-
{
821-
errmsg = libpq_gettext("extraneous data in \"D\" message");
822-
goto advance_and_error;
823-
}
824-
825-
/* Advance inStart to show that the "D" message has been processed. */
826-
conn->inStart = conn->inCursor;
827-
828792
/* Process the collected row */
829793
errmsg = NULL;
830794
if (pqRowProcessor(conn, &errmsg))
831795
return 0; /* normal, successful exit */
832796

833-
goto set_error_result; /* pqRowProcessor failed, report it */
797+
/* pqRowProcessor failed, fall through to report it */
834798

835799
advance_and_error:
836-
/* Discard the failed message by pretending we read it */
837-
conn->inStart += 5 + msgLength;
838-
839-
set_error_result:
840800

841801
/*
842802
* Replace partially constructed result with an error result. First
@@ -856,6 +816,12 @@ getAnotherTuple(PGconn *conn, int msgLength)
856816
printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
857817
pqSaveErrorResult(conn);
858818

819+
/*
820+
* Show the message as fully consumed, else pqParseInput3 will overwrite
821+
* our error with a complaint about that.
822+
*/
823+
conn->inCursor = conn->inStart + 5 + msgLength;
824+
859825
/*
860826
* Return zero to allow input parsing to continue. Subsequent "D"
861827
* messages will be ignored until we get to end of data, since an error

0 commit comments

Comments
 (0)