v5-0004-fixup-Don-t-use-deprecated-and-insecure-PQcancel-.patch
text/x-patch
Filename: v5-0004-fixup-Don-t-use-deprecated-and-insecure-PQcancel-.patch
Type: text/x-patch
Part: 4
Patch
Format: format-patch
Series: patch v5-0004
Subject: fixup! Don't use deprecated and insecure PQcancel psql and other tools anymore
| File | + | − |
|---|---|---|
| src/fe_utils/cancel.c | 37 | 9 |
From 3000ad8e1afcca05479ed2d5903214b96dc17227 Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <postgres@jeltef.nl>
Date: Sat, 7 Mar 2026 00:00:38 +0100
Subject: [PATCH v5 4/5] fixup! Don't use deprecated and insecure PQcancel psql
and other tools anymore
Keep track of generation that should be cancelled.
---
src/fe_utils/cancel.c | 46 ++++++++++++++++++++++++++++++++++---------
1 file changed, 37 insertions(+), 9 deletions(-)
diff --git a/src/fe_utils/cancel.c b/src/fe_utils/cancel.c
index 5d645c554ab..caaf3e7c675 100644
--- a/src/fe_utils/cancel.c
+++ b/src/fe_utils/cancel.c
@@ -46,6 +46,7 @@
#include "postgres_fe.h"
#include <signal.h>
+#include <stdatomic.h>
#include <unistd.h>
#ifndef WIN32
@@ -85,9 +86,22 @@ static PGcancelConn *cancelConn = NULL;
/*
* Generation counter for cancelConn. Incremented each time cancelConn is
- * changed. Used to detect if cancelConn was replaced while we were using it.
+ * changed (under cancelConnLock). Read by the signal handler (which can run
+ * on any thread) and by the cancel thread, so it needs to be atomic.
*/
-static uint64 cancelConnGeneration = 0;
+static _Atomic sig_atomic_t cancelConnGeneration = 0;
+
+/*
+ * The generation that was current when SIGINT was received. The cancel
+ * thread compares this against cancelConnGeneration before sending a cancel
+ * request, so that a stale signal (from a query that already finished) does
+ * not cancel a subsequent query.
+ *
+ * Written by the signal handler (or Windows console handler) and read by
+ * the cancel thread, so it must be an atomic type for cross-thread safety.
+ * sig_atomic_t is guaranteed lock-free and thus safe in signal handlers.
+ */
+static _Atomic sig_atomic_t cancelConnGenerationDuringSignal = 0;
/*
* Mutex protecting cancelConn and cancelConnGeneration.
@@ -151,22 +165,29 @@ static void
SendCancelRequest(void)
{
PGcancelConn *cc;
- uint64 generation;
+ sig_atomic_t generation;
bool putConnectionBack = false;
/*
* Borrow the cancel connection from the global, setting it to NULL so
* that SetCancelConn/ResetCancelConn won't free it while we're using it.
+ *
+ * Also check that the generation matches what the signal handler
+ * captured. If it doesn't, the main thread already moved on to a
+ * different query (or no query at all), so sending a cancel would be
+ * wrong.
*/
pthread_mutex_lock(&cancelConnLock);
cc = cancelConn;
- generation = cancelConnGeneration;
+ generation = atomic_load(&cancelConnGeneration);
+ if (cc == NULL || generation != atomic_load(&cancelConnGenerationDuringSignal))
+ {
+ pthread_mutex_unlock(&cancelConnLock);
+ return;
+ }
cancelConn = NULL;
pthread_mutex_unlock(&cancelConnLock);
- if (cc == NULL)
- return;
-
write_stderr(cancel_sent_msg);
if (!PQcancelBlocking(cc))
@@ -185,7 +206,7 @@ SendCancelRequest(void)
* using it.
*/
pthread_mutex_lock(&cancelConnLock);
- if (cancelConnGeneration == generation)
+ if (atomic_load(&cancelConnGeneration) == generation)
{
/* Generation unchanged, put it back for reuse */
cancelConn = cc;
@@ -210,7 +231,7 @@ SetCancelConnInternal(PGcancelConn *newCancelConn)
pthread_mutex_lock(&cancelConnLock);
oldCancelConn = cancelConn;
cancelConn = newCancelConn;
- cancelConnGeneration++;
+ atomic_fetch_add(&cancelConnGeneration, 1);
pthread_mutex_unlock(&cancelConnLock);
if (oldCancelConn != NULL)
@@ -256,6 +277,7 @@ consoleHandler(DWORD dwCtrlType)
dwCtrlType == CTRL_BREAK_EVENT)
{
CancelRequested = true;
+ atomic_store(&cancelConnGenerationDuringSignal, atomic_load(&cancelConnGeneration));
if (cancel_callback != NULL)
cancel_callback();
@@ -311,6 +333,12 @@ handle_sigint(SIGNAL_ARGS)
CancelRequested = true;
+ /*
+ * Capture the current generation so the cancel thread can verify it's
+ * still sending the cancel for the right query.
+ */
+ atomic_store(&cancelConnGenerationDuringSignal, atomic_load(&cancelConnGeneration));
+
if (cancel_callback != NULL)
cancel_callback();
--
2.53.0