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

Commit 56defbd

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 1cc33ab commit 56defbd

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
@@ -292,8 +292,6 @@ pqParseInput3(PGconn *conn)
292292
/* First 'T' in a query sequence */
293293
if (getRowDescriptions(conn, msgLength))
294294
return;
295-
/* getRowDescriptions() moves inStart itself */
296-
continue;
297295
}
298296
else
299297
{
@@ -339,17 +337,14 @@ pqParseInput3(PGconn *conn)
339337
case 't': /* Parameter Description */
340338
if (getParamDescriptions(conn, msgLength))
341339
return;
342-
/* getParamDescriptions() moves inStart itself */
343-
continue;
340+
break;
344341
case 'D': /* Data Row */
345342
if (conn->result != NULL &&
346343
conn->result->resultStatus == PGRES_TUPLES_OK)
347344
{
348345
/* Read another tuple of a normal query response */
349346
if (getAnotherTuple(conn, msgLength))
350347
return;
351-
/* getAnotherTuple() moves inStart itself */
352-
continue;
353348
}
354349
else if (conn->result != NULL &&
355350
conn->result->resultStatus == PGRES_FATAL_ERROR)
@@ -466,7 +461,6 @@ handleSyncLoss(PGconn *conn, char id, int msgLength)
466461
* command for a prepared statement) containing the attribute data.
467462
* Returns: 0 if processed message successfully, EOF to suspend parsing
468463
* (the latter case is not actually used currently).
469-
* In the former case, conn->inStart has been advanced past the message.
470464
*/
471465
static int
472466
getRowDescriptions(PGconn *conn, int msgLength)
@@ -571,19 +565,9 @@ getRowDescriptions(PGconn *conn, int msgLength)
571565
result->binary = 0;
572566
}
573567

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

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

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

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

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

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

702675
not_enough_data:
703-
PQclear(result);
704-
return EOF;
676+
errmsg = libpq_gettext("insufficient data in \"t\" message");
705677

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

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

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

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

832-
goto set_error_result; /* pqRowProcessor failed, report it */
796+
/* pqRowProcessor failed, fall through to report it */
833797

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

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

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

0 commit comments

Comments
 (0)