Make query cancellation keys longer

Heikki Linnakangas <hlinnaka@iki.fi>

From: Heikki Linnakangas <hlinnaka@iki.fi>
To: pgsql-hackers <pgsql-hackers@postgresql.org>
Date: 2024-02-29T21:25:43Z
Lists: pgsql-hackers

Attachments

Currently, cancel request key is a 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. The attached patch makes it longer. It is an 
optional protocol feature, so it's fully backwards-compatible with 
clients that don't support longer keys.

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 no longer hardcoded in the 
protocol. 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 could be useful. If the client doesn't request the 
protocol feature, the server generates a 32-bit key like before.

One complication with this was that because we no longer know how long 
the key should be, 4-bytes or something longer, until the backend has 
performed the protocol negotiation, we cannot generate the key in the 
postmaster before forking the process anymore. The first patch here 
changes things so that the cancellation key is generated later, in the 
backend, and the backend advertises the key in the PMSignalState array. 
This is similar to how this has always worked in EXEC_BACKEND mode with 
the ShmemBackendArray, but instead of having a separate array, I added 
fields to the PMSignalState slots. This removes a bunch of 
EXEC_BACKEND-specific code, which is nice.

Any thoughts on this? Documentation is still missing, and there's one 
TODO on adding a portable time-constant memcmp() function; I'll add 
those if there's agreement on this otherwise.

-- 
Heikki Linnakangas
Neon (https://neon.tech)

Commits

  1. Add timingsafe_bcmp(), for constant-time memory comparison

  2. Add missing declarations to pg_config.h.in

  3. docs: Add a new section and a table listing protocol versions

  4. Make cancel request keys longer

  5. libpq: Add min/max_protocol_version connection options

  6. libpq: Handle NegotiateProtocolVersion message differently

  7. docs: Update phrase on message lengths in the protocol

  8. libpq: Trace all NegotiateProtocolVersion fields

  9. libpq: Add PQfullProtocolVersion to exports.txt

  10. Move cancel key generation to after forking the backend