Skip to content

Commit f531701

Browse files
author
Commitfest Bot
committed
[CF 5859] v4 - Allow missing BackendKeyData message & enforce cancel key length
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5859 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/[email protected] Author(s): Jelte Fennema-Nio
2 parents 16a0039 + 00e2825 commit f531701

File tree

4 files changed

+111
-41
lines changed

4 files changed

+111
-41
lines changed

doc/src/sgml/protocol.sgml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4136,7 +4136,7 @@ psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;"
41364136
message, indicated by the length field.
41374137
</para>
41384138
<para>
4139-
The maximum key length is 256 bytes. The
4139+
The minimum and maximum key length are 4 and 256 bytes, respectively. The
41404140
<productname>PostgreSQL</productname> server only sends keys up to
41414141
32 bytes, but the larger maximum size allows for future server
41424142
versions, as well as connection poolers and other middleware, to use

src/interfaces/libpq/fe-exec.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1076,8 +1076,12 @@ pqSaveMessageField(PGresult *res, char code, const char *value)
10761076

10771077
/*
10781078
* pqSaveParameterStatus - remember parameter status sent by backend
1079+
*
1080+
* Returns 1 on success, 0 on out-of-memory. (Note that on out-of-memory, we
1081+
* have already released the old value of the parameter, if any. The only
1082+
* really safe way to recover is to terminate the connection.)
10791083
*/
1080-
void
1084+
int
10811085
pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
10821086
{
10831087
pgParameterStatus *pstatus;
@@ -1119,6 +1123,11 @@ pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
11191123
pstatus->next = conn->pstatus;
11201124
conn->pstatus = pstatus;
11211125
}
1126+
else
1127+
{
1128+
/* out of memory */
1129+
return 0;
1130+
}
11221131

11231132
/*
11241133
* Save values of settings that are of interest to libpq in fields of the
@@ -1190,6 +1199,8 @@ pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
11901199
{
11911200
conn->scram_sha_256_iterations = atoi(value);
11921201
}
1202+
1203+
return 1;
11931204
}
11941205

11951206

src/interfaces/libpq/fe-protocol3.c

Lines changed: 97 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
(id) == PqMsg_RowDescription)
4444

4545

46+
static void handleFatalError(PGconn *conn);
4647
static void handleSyncLoss(PGconn *conn, char id, int msgLength);
4748
static int getRowDescriptions(PGconn *conn, int msgLength);
4849
static int getParamDescriptions(PGconn *conn, int msgLength);
@@ -120,12 +121,12 @@ pqParseInput3(PGconn *conn)
120121
conn))
121122
{
122123
/*
123-
* XXX add some better recovery code... plan is to skip over
124-
* the message using its length, then report an error. For the
125-
* moment, just treat this like loss of sync (which indeed it
126-
* might be!)
124+
* Abandon the connection. There's not much else we can
125+
* safely do; we can't just ignore the message or we could
126+
* miss important changes to the connection state.
127+
* pqCheckInBufferSpace() already reported the error.
127128
*/
128-
handleSyncLoss(conn, id, msgLength);
129+
handleFatalError(conn);
129130
}
130131
return;
131132
}
@@ -456,6 +457,11 @@ pqParseInput3(PGconn *conn)
456457
/* Normal case: parsing agrees with specified length */
457458
pqParseDone(conn, conn->inCursor);
458459
}
460+
else if (conn->error_result && conn->status == CONNECTION_BAD)
461+
{
462+
/* The connection was abandoned and we already reported it */
463+
return;
464+
}
459465
else
460466
{
461467
/* Trouble --- report it */
@@ -470,15 +476,14 @@ pqParseInput3(PGconn *conn)
470476
}
471477

472478
/*
473-
* handleSyncLoss: clean up after loss of message-boundary sync
479+
* handleFatalError: clean up after a nonrecoverable error
474480
*
475-
* There isn't really a lot we can do here except abandon the connection.
481+
* This is for errors where we need to abandon the connection. The caller has
482+
* already saved the error message in conn->errorMessage.
476483
*/
477484
static void
478-
handleSyncLoss(PGconn *conn, char id, int msgLength)
485+
handleFatalError(PGconn *conn)
479486
{
480-
libpq_append_conn_error(conn, "lost synchronization with server: got message type \"%c\", length %d",
481-
id, msgLength);
482487
/* build an error result holding the error message */
483488
pqSaveErrorResult(conn);
484489
conn->asyncStatus = PGASYNC_READY; /* drop out of PQgetResult wait loop */
@@ -487,6 +492,19 @@ handleSyncLoss(PGconn *conn, char id, int msgLength)
487492
conn->status = CONNECTION_BAD; /* No more connection to backend */
488493
}
489494

495+
/*
496+
* handleSyncLoss: clean up after loss of message-boundary sync
497+
*
498+
* There isn't really a lot we can do here except abandon the connection.
499+
*/
500+
static void
501+
handleSyncLoss(PGconn *conn, char id, int msgLength)
502+
{
503+
libpq_append_conn_error(conn, "lost synchronization with server: got message type \"%c\", length %d",
504+
id, msgLength);
505+
handleFatalError(conn);
506+
}
507+
490508
/*
491509
* parseInput subroutine to read a 'T' (row descriptions) message.
492510
* We'll build a new PGresult structure (unless called for a Describe
@@ -1519,7 +1537,11 @@ getParameterStatus(PGconn *conn)
15191537
return EOF;
15201538
}
15211539
/* And save it */
1522-
pqSaveParameterStatus(conn, conn->workBuffer.data, valueBuf.data);
1540+
if (!pqSaveParameterStatus(conn, conn->workBuffer.data, valueBuf.data))
1541+
{
1542+
libpq_append_conn_error(conn, "out of memory");
1543+
handleFatalError(conn);
1544+
}
15231545
termPQExpBuffer(&valueBuf);
15241546
return 0;
15251547
}
@@ -1547,12 +1569,33 @@ getBackendKeyData(PGconn *conn, int msgLength)
15471569

15481570
cancel_key_len = 5 + msgLength - (conn->inCursor - conn->inStart);
15491571

1572+
if (cancel_key_len != 4 && conn->pversion == PG_PROTOCOL(3, 0))
1573+
{
1574+
libpq_append_conn_error(conn, "received invalid BackendKeyData message: cancel key length %d is different from 4, which is not supported in version 3.0 of the protocol", cancel_key_len);
1575+
handleFatalError(conn);
1576+
return 0;
1577+
}
1578+
1579+
if (cancel_key_len < 4)
1580+
{
1581+
libpq_append_conn_error(conn, "received invalid BackendKeyData message: cancel key length %d is below minimum of 4 bytes", cancel_key_len);
1582+
handleFatalError(conn);
1583+
return 0;
1584+
}
1585+
1586+
if (cancel_key_len > 256)
1587+
{
1588+
libpq_append_conn_error(conn, "received invalid BackendKeyData message: cancel key length %d exceeds maximum of 256 bytes", cancel_key_len);
1589+
handleFatalError(conn);
1590+
return 0;
1591+
}
1592+
15501593
conn->be_cancel_key = malloc(cancel_key_len);
15511594
if (conn->be_cancel_key == NULL)
15521595
{
15531596
libpq_append_conn_error(conn, "out of memory");
1554-
/* discard the message */
1555-
return EOF;
1597+
handleFatalError(conn);
1598+
return 0;
15561599
}
15571600
if (pqGetnchar(conn->be_cancel_key, cancel_key_len, conn))
15581601
{
@@ -1589,10 +1632,21 @@ getNotify(PGconn *conn)
15891632
/* must save name while getting extra string */
15901633
svname = strdup(conn->workBuffer.data);
15911634
if (!svname)
1592-
return EOF;
1635+
{
1636+
/*
1637+
* Notify messages can arrive at any state, so we cannot associate the
1638+
* error with any particular query. There's no way to return back an
1639+
* "async error", so the best we can do is drop the connection. That
1640+
* seems better than silently ignoring the notification.
1641+
*/
1642+
libpq_append_conn_error(conn, "out of memory");
1643+
handleFatalError(conn);
1644+
return 0;
1645+
}
15931646
if (pqGets(&conn->workBuffer, conn))
15941647
{
1595-
free(svname);
1648+
if (svname)
1649+
free(svname);
15961650
return EOF;
15971651
}
15981652

@@ -1604,21 +1658,26 @@ getNotify(PGconn *conn)
16041658
nmlen = strlen(svname);
16051659
extralen = strlen(conn->workBuffer.data);
16061660
newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + extralen + 2);
1607-
if (newNotify)
1608-
{
1609-
newNotify->relname = (char *) newNotify + sizeof(PGnotify);
1610-
strcpy(newNotify->relname, svname);
1611-
newNotify->extra = newNotify->relname + nmlen + 1;
1612-
strcpy(newNotify->extra, conn->workBuffer.data);
1613-
newNotify->be_pid = be_pid;
1614-
newNotify->next = NULL;
1615-
if (conn->notifyTail)
1616-
conn->notifyTail->next = newNotify;
1617-
else
1618-
conn->notifyHead = newNotify;
1619-
conn->notifyTail = newNotify;
1661+
if (!newNotify)
1662+
{
1663+
free(svname);
1664+
libpq_append_conn_error(conn, "out of memory");
1665+
handleFatalError(conn);
1666+
return 0;
16201667
}
16211668

1669+
newNotify->relname = (char *) newNotify + sizeof(PGnotify);
1670+
strcpy(newNotify->relname, svname);
1671+
newNotify->extra = newNotify->relname + nmlen + 1;
1672+
strcpy(newNotify->extra, conn->workBuffer.data);
1673+
newNotify->be_pid = be_pid;
1674+
newNotify->next = NULL;
1675+
if (conn->notifyTail)
1676+
conn->notifyTail->next = newNotify;
1677+
else
1678+
conn->notifyHead = newNotify;
1679+
conn->notifyTail = newNotify;
1680+
16221681
free(svname);
16231682
return 0;
16241683
}
@@ -1752,12 +1811,12 @@ getCopyDataMessage(PGconn *conn)
17521811
conn))
17531812
{
17541813
/*
1755-
* XXX add some better recovery code... plan is to skip over
1756-
* the message using its length, then report an error. For the
1757-
* moment, just treat this like loss of sync (which indeed it
1758-
* might be!)
1814+
* Abandon the connection. There's not much else we can
1815+
* safely do; we can't just ignore the message or we could
1816+
* miss important changes to the connection state.
1817+
* pqCheckInBufferSpace() already reported the error.
17591818
*/
1760-
handleSyncLoss(conn, id, msgLength);
1819+
handleFatalError(conn);
17611820
return -2;
17621821
}
17631822
return 0;
@@ -2186,12 +2245,12 @@ pqFunctionCall3(PGconn *conn, Oid fnid,
21862245
conn))
21872246
{
21882247
/*
2189-
* XXX add some better recovery code... plan is to skip over
2190-
* the message using its length, then report an error. For the
2191-
* moment, just treat this like loss of sync (which indeed it
2192-
* might be!)
2248+
* Abandon the connection. There's not much else we can
2249+
* safely do; we can't just ignore the message or we could
2250+
* miss important changes to the connection state.
2251+
* pqCheckInBufferSpace() already reported the error.
21932252
*/
2194-
handleSyncLoss(conn, id, msgLength);
2253+
handleFatalError(conn);
21952254
break;
21962255
}
21972256
continue;

src/interfaces/libpq/libpq-int.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,7 @@ extern PGresult *pqPrepareAsyncResult(PGconn *conn);
746746
extern void pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...) pg_attribute_printf(2, 3);
747747
extern void pqSaveMessageField(PGresult *res, char code,
748748
const char *value);
749-
extern void pqSaveParameterStatus(PGconn *conn, const char *name,
749+
extern int pqSaveParameterStatus(PGconn *conn, const char *name,
750750
const char *value);
751751
extern int pqRowProcessor(PGconn *conn, const char **errmsgp);
752752
extern void pqCommandQueueAdvance(PGconn *conn, bool isReadyForQuery,

0 commit comments

Comments
 (0)