From 32513dd0c5b48e4d6ac56f8a53085480c30a94bf Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Thu, 29 Feb 2024 23:22:08 +0200 Subject: [PATCH 2/2] Make cancel request keys longer, as an optional protocol feature Currently, cancel request key is 32-bit token, which isn't very much entropy. If you want to cancel another session's query, you can brute-force it. In most environments, an unauthorized cancellation of a query isn't very serious, but it nevertheless would be nice to have more protection from it. To make it harder to guess the key, make it larger. But because the length is coded in the FE-BE protocol, make it an optional protocol feature. If the client requests the "_pq_.extended_query_cancel" protocol feature, the server will generate a longer 256-bit cancellation key. However, the new longer key length is not hardcoded in the protocol anymore, the client is expected to deal with variable length keys, up to some reasonable upper limit (TODO: document the maximum). This flexibility allows e.g. a connection pooler to add more information to the cancel key, which might be useful for finding the connection. If the client doesn't request the optional protocol feature, the server generates a 32-bit key like before. --- src/backend/access/transam/parallel.c | 6 +--- src/backend/postmaster/postmaster.c | 52 +++++++++++++++------------ src/backend/storage/ipc/pmsignal.c | 29 +++++++++++---- src/backend/storage/lmgr/proc.c | 9 +++-- src/backend/tcop/postgres.c | 2 +- src/backend/utils/init/globals.c | 1 - src/include/libpq/libpq-be.h | 5 +++ src/include/libpq/pqcomm.h | 5 +-- src/include/miscadmin.h | 3 +- src/include/storage/pmsignal.h | 6 ++-- src/interfaces/libpq/fe-cancel.c | 34 +++++++++--------- src/interfaces/libpq/fe-connect.c | 26 ++++++++++---- src/interfaces/libpq/fe-protocol3.c | 32 ++++++++++++++--- src/interfaces/libpq/libpq-int.h | 10 ++++-- 14 files changed, 148 insertions(+), 72 deletions(-) diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c index 849a03e4b65..652b96225a4 100644 --- a/src/backend/access/transam/parallel.c +++ b/src/backend/access/transam/parallel.c @@ -1135,7 +1135,6 @@ HandleParallelMessage(ParallelContext *pcxt, int i, StringInfo msg) { int32 pid = pq_getmsgint(msg, 4); - (void) pq_getmsgint(msg, 4); /* discard cancel key */ (void) pq_getmsgend(msg); pcxt->worker[i].pid = pid; break; @@ -1372,13 +1371,10 @@ ParallelWorkerMain(Datum main_arg) /* * Send a BackendKeyData message to the process that initiated parallelism * so that it has access to our PID before it receives any other messages - * from us. Our cancel key is sent, too, since that's the way the - * protocol message is defined, but it won't actually be used for anything - * in this case. + * from us. */ pq_beginmessage(&msgbuf, PqMsg_BackendKeyData); pq_sendint32(&msgbuf, (int32) MyProcPid); - pq_sendint32(&msgbuf, (int32) MyCancelKey); pq_endmessage(&msgbuf); /* diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 408f8fb9b6d..47c29e8c10e 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -422,7 +422,7 @@ static int ServerLoop(void); static int BackendStartup(Port *port); static int ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done); static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options); -static void processCancelRequest(Port *port, void *pkt); +static void processCancelRequest(Port *port, void *pkt, int pktlen); static void report_fork_failure_to_client(Port *port, int errnum); static CAC_state canAcceptConnections(int backend_type); static void signal_child(pid_t pid, int signal); @@ -501,7 +501,7 @@ typedef struct BackgroundWorker bgworker; char DataDir[MAXPGPATH]; - int32 MyCancelKey; + char MyCancelKey[CANCEL_KEY_LENGTH]; int MyPMChildSlot; #ifndef WIN32 unsigned long UsedShmemSegID; @@ -2008,16 +2008,9 @@ ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done) */ port->proto = proto = pg_ntoh32(*((ProtocolVersion *) buf)); - if (proto == CANCEL_REQUEST_CODE) + if (proto == EXTENDED_CANCEL_REQUEST_CODE || proto == CANCEL_REQUEST_CODE) { - if (len != sizeof(CancelRequestPacket)) - { - ereport(COMMERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("invalid length of startup packet"))); - return STATUS_ERROR; - } - processCancelRequest(port, buf); + processCancelRequest(port, buf, len); /* Not really an error, but we don't want to proceed further */ return STATUS_ERROR; } @@ -2202,11 +2195,16 @@ retry1: { /* * Any option beginning with _pq_. is reserved for use as a - * protocol-level option, but at present no such options are - * defined. + * protocol-level option. At present, there is only this one. */ - unrecognized_protocol_options = - lappend(unrecognized_protocol_options, pstrdup(nameptr)); + if (strcmp(nameptr + 5, "extended_query_cancel") == 0) + { + /* the value is ignored */ + port->extended_query_cancel = true; + } + else + unrecognized_protocol_options = + lappend(unrecognized_protocol_options, pstrdup(nameptr)); } else { @@ -2319,16 +2317,27 @@ SendNegotiateProtocolVersion(List *unrecognized_protocol_options) * Nothing is sent back to the client. */ static void -processCancelRequest(Port *port, void *pkt) +processCancelRequest(Port *port, void *pkt, int pktlen) { CancelRequestPacket *canc = (CancelRequestPacket *) pkt; - int backendPID; - int32 cancelAuthCode; + int len; - backendPID = (int) pg_ntoh32(canc->backendPID); - cancelAuthCode = (int32) pg_ntoh32(canc->cancelAuthCode); + if (pktlen < offsetof(CancelRequestPacket, cancelAuthCode)) + { + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("invalid length of extended quer cancel packet"))); + return; + } + canc = (CancelRequestPacket *) pkt; + len = pktlen - offsetof(CancelRequestPacket, cancelAuthCode); + + if (len != 4 && canc->cancelRequestCode == CANCEL_REQUEST_CODE) + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("invalid length of cancel request packet"))); - SendCancelRequest(backendPID, cancelAuthCode); + SendCancelRequest(pg_ntoh32(canc->backendPID), canc->cancelAuthCode, len); } /* @@ -5981,7 +5990,6 @@ save_backend_variables(BackendParameters *param, Port *port, BackgroundWorker *w strlcpy(param->DataDir, DataDir, MAXPGPATH); - param->MyCancelKey = MyCancelKey; param->MyPMChildSlot = MyPMChildSlot; #ifdef WIN32 diff --git a/src/backend/storage/ipc/pmsignal.c b/src/backend/storage/ipc/pmsignal.c index 9516b869cb2..79751b513a0 100644 --- a/src/backend/storage/ipc/pmsignal.c +++ b/src/backend/storage/ipc/pmsignal.c @@ -72,7 +72,8 @@ typedef struct ChildSlotData pg_atomic_uint32 state; int pid; - int32 cancel_key; + int cancel_key_len; + char cancel_key[MAX_CANCEL_KEY_LENGTH]; } ChildSlotData; /* "typedef struct PMSignalData PMSignalData" appears in pmsignal.h */ @@ -328,16 +329,18 @@ IsPostmasterChildWalSender(int slot) * actively using shared memory. This is called in the child process. */ void -MarkPostmasterChildActive(int pid, int32 cancelAuthCode) +MarkPostmasterChildActive(int pid, char *cancelKey, int len) { int slot = MyPMChildSlot; + Assert(len <= MAX_CANCEL_KEY_LENGTH); Assert(slot > 0 && slot <= PMSignalState->num_child_slots); slot--; Assert(pg_atomic_read_u32(&PMSignalState->child_slots[slot].state) == PM_CHILD_ASSIGNED); PMSignalState->child_slots[slot].pid = pid; - PMSignalState->child_slots[slot].cancel_key = cancelAuthCode; - pg_memory_barrier(); + memcpy(PMSignalState->child_slots[slot].cancel_key, cancelKey, len); + PMSignalState->child_slots[slot].cancel_key_len = len; + pg_write_barrier(); pg_atomic_write_u32(&PMSignalState->child_slots[slot].state, PM_CHILD_ACTIVE); } @@ -478,8 +481,18 @@ PostmasterDeathSignalInit(void) #endif /* USE_POSTMASTER_DEATH_SIGNAL */ } +static int +pg_const_time_memcmp(const void *a, const void *b, size_t len) +{ + /* + * FIXME: need a constant time implementation. Implement one somewhere in + * src/port. + */ + return memcmp(a, b, len); +} + void -SendCancelRequest(int backendPID, int32 cancelAuthCode) +SendCancelRequest(int backendPID, char *cancelKey, int len) { /* * See if we have a matching backend. In the EXEC_BACKEND case, we can no @@ -497,7 +510,11 @@ SendCancelRequest(int backendPID, int32 cancelAuthCode) pg_read_barrier(); if (slot->pid == backendPID) { - if (slot->cancel_key == cancelAuthCode) + /* + * Use pg_const_time_memcmp() to prevent an attacker from using + * timing to reveal the cancel key. + */ + if (len == slot->cancel_key_len && pg_const_time_memcmp(slot->cancel_key, cancelKey, len) == 0) { /* Found a match; signal that backend to cancel current op */ ereport(DEBUG2, diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index 5984fe7d44c..7e4cce4c91f 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -36,6 +36,7 @@ #include "access/transam.h" #include "access/twophase.h" #include "access/xlogutils.h" +#include "libpq/libpq-be.h" #include "miscadmin.h" #include "pgstat.h" #include "postmaster/autovacuum.h" @@ -68,6 +69,9 @@ bool log_lock_waits = false; PGPROC *MyProc = NULL; int MyProcNumber = INVALID_PGPROCNO; +char MyCancelKey[MAX_CANCEL_KEY_LENGTH]; +int MyCancelKeyLength; + /* * This spinlock protects the freelist of recycled PGPROC structures. * We cannot use an LWLock because the LWLock manager depends on already @@ -317,7 +321,8 @@ InitProcess(void) * better have something random in the field to prevent unfriendly people * from sending cancels to them. */ - if (!pg_strong_random(&MyCancelKey, sizeof(int32))) + MyCancelKeyLength = (MyProcPort != NULL && MyProcPort->extended_query_cancel) ? MAX_CANCEL_KEY_LENGTH : 4; + if (!pg_strong_random(&MyCancelKey, MyCancelKeyLength)) { ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), @@ -387,7 +392,7 @@ InitProcess(void) */ if (IsUnderPostmaster && !IsAutoVacuumLauncherProcess() && !IsLogicalSlotSyncWorker()) - MarkPostmasterChildActive(MyProcPid, MyCancelKey); + MarkPostmasterChildActive(MyProcPid, MyCancelKey, MyCancelKeyLength); /* * Initialize all fields of MyProc, except for those previously diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 59ab812d2e8..7164aa31978 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -4274,7 +4274,7 @@ PostgresMain(const char *dbname, const char *username) pq_beginmessage(&buf, PqMsg_BackendKeyData); pq_sendint32(&buf, (int32) MyProcPid); - pq_sendint32(&buf, (int32) MyCancelKey); + pq_sendbytes(&buf, MyCancelKey, MyCancelKeyLength); pq_endmessage(&buf); /* Need not flush since ReadyForQuery will do it. */ } diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c index 5eaee88d969..7efcdae9657 100644 --- a/src/backend/utils/init/globals.c +++ b/src/backend/utils/init/globals.c @@ -46,7 +46,6 @@ int MyProcPid; pg_time_t MyStartTime; TimestampTz MyStartTimestamp; struct Port *MyProcPort; -int32 MyCancelKey; int MyPMChildSlot; /* diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index 47d66d55241..3aebf92a754 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -175,6 +175,11 @@ typedef struct Port */ char *application_name; + /* + * Protocol options supported by the client + */ + bool extended_query_cancel; + /* * Information that needs to be held during the authentication cycle. */ diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h index 9ae469c86c4..9b5579a5386 100644 --- a/src/include/libpq/pqcomm.h +++ b/src/include/libpq/pqcomm.h @@ -129,6 +129,7 @@ typedef uint32 AuthRequest; * The cancel request code must not match any protocol version number * we're ever likely to use. This random choice should do. */ +#define EXTENDED_CANCEL_REQUEST_CODE PG_PROTOCOL(1234,5677) #define CANCEL_REQUEST_CODE PG_PROTOCOL(1234,5678) typedef struct CancelRequestPacket @@ -136,10 +137,10 @@ typedef struct CancelRequestPacket /* Note that each field is stored in network byte order! */ MsgType cancelRequestCode; /* code to identify a cancel request */ uint32 backendPID; /* PID of client's backend */ - uint32 cancelAuthCode; /* secret key to authorize cancel */ + char cancelAuthCode[FLEXIBLE_ARRAY_MEMBER]; /* secret key to + * authorize cancel */ } CancelRequestPacket; - /* * A client can also start by sending a SSL or GSSAPI negotiation request to * get a secure channel. diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 756d144c323..b70b412ea90 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -192,7 +192,8 @@ extern PGDLLIMPORT pg_time_t MyStartTime; extern PGDLLIMPORT TimestampTz MyStartTimestamp; extern PGDLLIMPORT struct Port *MyProcPort; extern PGDLLIMPORT struct Latch *MyLatch; -extern PGDLLIMPORT int32 MyCancelKey; +extern PGDLLIMPORT char MyCancelKey[]; +extern PGDLLIMPORT int MyCancelKeyLength; extern PGDLLIMPORT int MyPMChildSlot; extern PGDLLIMPORT char OutputFileName[]; diff --git a/src/include/storage/pmsignal.h b/src/include/storage/pmsignal.h index 8dff47e86e4..94420ecb0b5 100644 --- a/src/include/storage/pmsignal.h +++ b/src/include/storage/pmsignal.h @@ -57,6 +57,8 @@ typedef enum /* PMSignalData is an opaque struct, details known only within pmsignal.c */ typedef struct PMSignalData PMSignalData; +#define MAX_CANCEL_KEY_LENGTH 32 + /* * prototypes for functions in pmsignal.c */ @@ -69,12 +71,12 @@ extern QuitSignalReason GetQuitSignalReason(void); extern int AssignPostmasterChildSlot(void); extern bool ReleasePostmasterChildSlot(int slot); extern bool IsPostmasterChildWalSender(int slot); -extern void MarkPostmasterChildActive(int pid, int32 cancelAuthCode); +extern void MarkPostmasterChildActive(int pid, char *cancelKey, int len); extern void MarkPostmasterChildInactive(void); extern void MarkPostmasterChildWalSender(void); extern bool PostmasterIsAliveInternal(void); extern void PostmasterDeathSignalInit(void); -extern void SendCancelRequest(int backendPID, int32 cancelAuthCode); +extern void SendCancelRequest(int backendPID, char *cancelKey, int len); /* diff --git a/src/interfaces/libpq/fe-cancel.c b/src/interfaces/libpq/fe-cancel.c index 51f8d8a78c4..6c9bfeaa33f 100644 --- a/src/interfaces/libpq/fe-cancel.c +++ b/src/interfaces/libpq/fe-cancel.c @@ -34,6 +34,8 @@ PGcancel * PQgetCancel(PGconn *conn) { PGcancel *cancel; + int cancel_req_len; + CancelRequestPacket *req; if (!conn) return NULL; @@ -41,13 +43,13 @@ PQgetCancel(PGconn *conn) if (conn->sock == PGINVALID_SOCKET) return NULL; - cancel = malloc(sizeof(PGcancel)); + cancel_req_len = offsetof(CancelRequestPacket, cancelAuthCode) + conn->be_cancel_key_len; + cancel = malloc(offsetof(PGcancel, cancel_req) + cancel_req_len); if (cancel == NULL) return NULL; memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr)); - cancel->be_pid = conn->be_pid; - cancel->be_key = conn->be_key; + /* We use -1 to indicate an unset connection option */ cancel->pgtcp_user_timeout = -1; cancel->keepalives = -1; @@ -90,6 +92,13 @@ PQgetCancel(PGconn *conn) goto fail; } + req = (CancelRequestPacket *) &cancel->cancel_req; + req->cancelRequestCode = (MsgType) pg_hton32(conn->be_cancel_key_len == 4 ? CANCEL_REQUEST_CODE : EXTENDED_CANCEL_REQUEST_CODE); + req->backendPID = pg_hton32(conn->be_pid); + memcpy(req->cancelAuthCode, conn->be_cancel_key, conn->be_cancel_key_len); + /* include the length field itself in the length */ + cancel->cancel_pkt_len = pg_hton32(cancel_req_len + 4); + return cancel; fail: @@ -150,11 +159,8 @@ PQcancel(PGcancel *cancel, char *errbuf, int errbufsize) int save_errno = SOCK_ERRNO; pgsocket tmpsock = PGINVALID_SOCKET; int maxlen; - struct - { - uint32 packetlen; - CancelRequestPacket cp; - } crp; + char recvbuf; + int cancel_pkt_len; if (!cancel) { @@ -256,15 +262,11 @@ retry3: goto cancel_errReturn; } - /* Create and send the cancel request packet. */ - - crp.packetlen = pg_hton32((uint32) sizeof(crp)); - crp.cp.cancelRequestCode = (MsgType) pg_hton32(CANCEL_REQUEST_CODE); - crp.cp.backendPID = pg_hton32(cancel->be_pid); - crp.cp.cancelAuthCode = pg_hton32(cancel->be_key); + /* Send the cancel request packet. */ + cancel_pkt_len = pg_ntoh32(cancel->cancel_pkt_len); retry4: - if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp)) + if (send(tmpsock, &cancel->cancel_pkt_len, cancel_pkt_len, 0) != cancel_pkt_len) { if (SOCK_ERRNO == EINTR) /* Interrupted system call - we'll just try again */ @@ -281,7 +283,7 @@ retry4: * read to obtain any data, we are just waiting for EOF to be signaled. */ retry5: - if (recv(tmpsock, (char *) &crp, 1, 0) < 0) + if (recv(tmpsock, &recvbuf, 1, 0) < 0) { if (SOCK_ERRNO == EINTR) /* Interrupted system call - we'll just try again */ diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index d4e10a0c4f3..5148b24ba34 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -617,7 +617,12 @@ pqDropServerData(PGconn *conn) free(conn->write_err_msg); conn->write_err_msg = NULL; conn->be_pid = 0; - conn->be_key = 0; + if (conn->be_cancel_key != NULL) + { + free(conn->be_cancel_key); + conn->be_cancel_key = NULL; + } + conn->be_cancel_key_len = 0; } @@ -3724,14 +3729,21 @@ keep_going: /* We will come back to here until there is } else if (beresp == PqMsg_NegotiateProtocolVersion) { - if (pqGetNegotiateProtocolVersion3(conn)) + switch (pqGetNegotiateProtocolVersion3(conn)) { - libpq_append_conn_error(conn, "received invalid protocol negotiation message"); - goto error_return; + case 0: + /* OK, we read the message; mark data consumed */ + conn->inStart = conn->inCursor; + /* Stay in the CONNECTION_AWAITING_RESPONSE state */ + goto keep_going; + case 1: + /* OK, we read the message; mark data consumed */ + conn->inStart = conn->inCursor; + goto error_return; + case EOF: + /* We'll come back when there is more data */ + return PGRES_POLLING_READING; } - /* OK, we read the message; mark data consumed */ - conn->inStart = conn->inCursor; - goto error_return; } /* It is an authentication request. */ diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index 701d58e1087..c079297f736 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -310,8 +310,22 @@ pqParseInput3(PGconn *conn) */ if (pqGetInt(&(conn->be_pid), 4, conn)) return; - if (pqGetInt(&(conn->be_key), 4, conn)) - return; + + { + int cancel_key_len = 5 + msgLength - (conn->inCursor - conn->inStart); + + 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 */ + conn->inCursor = conn->inStart + 5 + msgLength; + break; + } + if (pqGetnchar(conn->be_cancel_key, cancel_key_len, conn)) + return; + conn->be_cancel_key_len = cancel_key_len; + } break; case PqMsg_RowDescription: if (conn->error_result || @@ -1404,7 +1418,8 @@ reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding) /* * Attempt to read a NegotiateProtocolVersion message. * Entry: 'v' message type and length have already been consumed. - * Exit: returns 0 if successfully consumed message. + * Exit: returns 0 if successfully consumed message and the negotiation succeeded. + * returns 1 if successfully consumed message and the negotiation failed. * returns EOF if not enough data. */ int @@ -1413,6 +1428,7 @@ pqGetNegotiateProtocolVersion3(PGconn *conn) int tmp; ProtocolVersion their_version; int num; + int num_required_missing = 0; PQExpBufferData buf; if (pqGetInt(&tmp, 4, conn) != 0) @@ -1430,16 +1446,23 @@ pqGetNegotiateProtocolVersion3(PGconn *conn) termPQExpBuffer(&buf); return EOF; } + if (strcmp(conn->workBuffer.data, "_pq_.extended_query_cancel") == 0) + { + /* that's ok */ + continue; + } + if (buf.len > 0) appendPQExpBufferChar(&buf, ' '); appendPQExpBufferStr(&buf, conn->workBuffer.data); + num_required_missing++; } if (their_version < conn->pversion) libpq_append_conn_error(conn, "protocol version not supported by server: client uses %u.%u, server supports up to %u.%u", PG_PROTOCOL_MAJOR(conn->pversion), PG_PROTOCOL_MINOR(conn->pversion), PG_PROTOCOL_MAJOR(their_version), PG_PROTOCOL_MINOR(their_version)); - if (num > 0) + if (num_required_missing > 0) { appendPQExpBuffer(&conn->errorMessage, libpq_ngettext("protocol extension not supported by server: %s", @@ -2312,6 +2335,7 @@ build_startup_packet(const PGconn *conn, char *packet, ADD_STARTUP_OPTION(next_eo->pgName, val); } } + ADD_STARTUP_OPTION("_pq_.extended_query_cancel", ""); /* Add trailing terminator */ if (packet) diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 82c18f870d2..6187f282fb0 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -491,8 +491,9 @@ struct pg_conn bool send_appname; /* okay to send application_name? */ /* Miscellaneous stuff */ - int be_pid; /* PID of backend --- needed for cancels */ - int be_key; /* key of backend --- needed for cancels */ + int be_pid; /* PID of backend --- needed for XX cancels */ + char *be_cancel_key; + int be_cancel_key_len; pgParameterStatus *pstatus; /* ParameterStatus data */ int client_encoding; /* encoding id */ bool std_strings; /* standard_conforming_strings */ @@ -629,7 +630,6 @@ struct pg_cancel { SockAddr raddr; /* Remote address */ int be_pid; /* PID of backend --- needed for cancels */ - int be_key; /* key of backend --- needed for cancels */ int pgtcp_user_timeout; /* tcp user timeout */ int keepalives; /* use TCP keepalives? */ int keepalives_idle; /* time between TCP keepalives */ @@ -637,6 +637,10 @@ struct pg_cancel * retransmits */ int keepalives_count; /* maximum number of TCP keepalive * retransmits */ + + /* Pre-constructed cancel request packet starts here */ + int32 cancel_pkt_len; /* in network-byte-order */ + char cancel_req[FLEXIBLE_ARRAY_MEMBER]; /* CancelRequestPacket */ }; -- 2.39.2