v3-0002-libpq-Be-strict-about-accept-cancel-key-lengths.patch
text/x-patch
Filename: v3-0002-libpq-Be-strict-about-accept-cancel-key-lengths.patch
Type: text/x-patch
Part: 1
Message:
Re: BackendKeyData is mandatory?
Patch
Format: format-patch
Series: patch v3-0002
Subject: libpq: Be strict about accept cancel key lengths
| File | + | − |
|---|---|---|
| doc/src/sgml/protocol.sgml | 1 | 1 |
| src/interfaces/libpq/fe-connect.c | 3 | 0 |
| src/interfaces/libpq/fe-protocol3.c | 28 | 2 |
From b8624b137ba291eaa422e7a5086d6a24801d9ce5 Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <github-tech@jeltef.nl>
Date: Wed, 25 Jun 2025 08:54:15 +0200
Subject: [PATCH v3 2/2] libpq: Be strict about accept cancel key lengths
The protocol documentation states that the maximum length of a cancel key
is 256 bytes. This starts checking for that limit in libpq. Otherwise
third party backend implementations will probably start using more bytes
anyway. We also start requiring that a protocol 3.0 connection does not
send a longer cancel key, to make sure that servers don't start breaking
old 3.0-only clients by accident. Finally this also restricts the
minimum key length to 4 bytes (both in the protocol spec and in the
libpq implementation).
---
doc/src/sgml/protocol.sgml | 2 +-
src/interfaces/libpq/fe-connect.c | 3 +++
src/interfaces/libpq/fe-protocol3.c | 30 +++++++++++++++++++++++++++--
3 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 0eb96360134..982f4a8d210 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -4160,7 +4160,7 @@ psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;"
message, indicated by the length field.
</para>
<para>
- The maximum key length is 256 bytes. The
+ The minimum and maximum key length are 4 and 256 bytes respectively. The
<productname>PostgreSQL</productname> server only sends keys up to
32 bytes, but the larger maximum size allows for future server
versions, as well as connection poolers and other middleware, to use
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 51a9c416584..f094611fe0d 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -4322,6 +4322,9 @@ keep_going: /* We will come back to here until there is
if (PQisBusy(conn))
return PGRES_POLLING_READING;
+ if (conn->status == CONNECTION_BAD)
+ goto error_return;
+
res = PQgetResult(conn);
/*
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 1599de757d1..818e7a47191 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -1547,13 +1547,31 @@ getBackendKeyData(PGconn *conn, int msgLength)
cancel_key_len = 5 + msgLength - (conn->inCursor - conn->inStart);
+ if (cancel_key_len != 4 && conn->pversion == PG_PROTOCOL(3, 0))
+ {
+ 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);
+ goto failure;
+ }
+
+ if (cancel_key_len < 4)
+ {
+ libpq_append_conn_error(conn, "received invalid BackendKeyData message: cancel key length %d is below minimum of 4 bytes", cancel_key_len);
+ goto failure;
+ }
+
+ if (cancel_key_len > 256)
+ {
+ libpq_append_conn_error(conn, "received invalid BackendKeyData message: cancel key length %d exceeds maximum of 256 bytes", cancel_key_len);
+ goto failure;
+ }
+
conn->be_cancel_key = malloc(cancel_key_len);
if (conn->be_cancel_key == NULL)
{
libpq_append_conn_error(conn, "out of memory");
- /* discard the message */
- return EOF;
+ goto failure;
}
+
if (pqGetnchar(conn->be_cancel_key, cancel_key_len, conn))
{
free(conn->be_cancel_key);
@@ -1562,6 +1580,14 @@ getBackendKeyData(PGconn *conn, int msgLength)
}
conn->be_cancel_key_len = cancel_key_len;
return 0;
+
+failure:
+ pqSaveErrorResult(conn);
+ conn->asyncStatus = PGASYNC_READY; /* drop out of PQgetResult wait loop */
+ /* flush input data since we're giving up on processing it */
+ pqDropConnection(conn, true);
+ conn->status = CONNECTION_BAD; /* No more connection to backend */
+ return EOF;
}
--
2.43.0