]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
fix: prevent usage of finished PGcancelConn
authorDenis Laxalde <denis.laxalde@dalibo.com>
Fri, 5 Apr 2024 08:47:47 +0000 (10:47 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Fri, 5 Apr 2024 18:35:16 +0000 (18:35 +0000)
Similarly to PGconn, PGcancelConn must not be used after .finish(); we
raise an OperationalError to prevent segfault when the connection
pointer is NULL.

psycopg/psycopg/pq/pq_ctypes.py
psycopg_c/psycopg_c/pq/pgcancel.pyx
tests/pq/test_pgconn.py

index a2fabbbab21712c6df0908e719d5d8b3431f64ed..ee3b51c19c55eb7ba3ea52b8174e4a2ba63b97e3 100644 (file)
@@ -944,6 +944,7 @@ class PGcancelConn:
             )
 
     def poll(self) -> int:
+        self._ensure_pgcancelconn()
         return impl.PQcancelPoll(self.pgcancelconn_ptr)
 
     @property
@@ -962,6 +963,7 @@ class PGcancelConn:
         return impl.PQcancelErrorMessage(self.pgcancelconn_ptr).decode()
 
     def reset(self) -> None:
+        self._ensure_pgcancelconn()
         impl.PQcancelReset(self.pgcancelconn_ptr)
 
     def finish(self) -> None:
@@ -976,6 +978,10 @@ class PGcancelConn:
         if p:
             PQcancelFinish(p)
 
+    def _ensure_pgcancelconn(self) -> None:
+        if not self.pgcancelconn_ptr:
+            raise e.OperationalError("the cancel connection is closed")
+
 
 class PGcancel:
     """
index 2922fd765827aa29aea735380090d9ea68ce7fd3..ab64763fa11cfdd2a4ec7482ea6c8ea12580c4db 100644 (file)
@@ -41,6 +41,7 @@ cdef class PGcancelConn:
             )
 
     def poll(self) -> int:
+        self._ensure_pgcancelconn()
         return libpq.PQcancelPoll(self.pgcancelconn_ptr)
 
     @property
@@ -59,6 +60,7 @@ cdef class PGcancelConn:
         return libpq.PQcancelErrorMessage(self.pgcancelconn_ptr).decode()
 
     def reset(self) -> None:
+        self._ensure_pgcancelconn()
         libpq.PQcancelReset(self.pgcancelconn_ptr)
 
     def finish(self) -> None:
@@ -66,6 +68,10 @@ cdef class PGcancelConn:
             libpq.PQcancelFinish(self.pgcancelconn_ptr)
             self.pgcancelconn_ptr = NULL
 
+    def _ensure_pgcancelconn(self) -> None:
+        if self.pgcancelconn_ptr is NULL:
+            raise e.OperationalError("the cancel connection is closed")
+
 
 cdef class PGcancel:
     def __cinit__(self):
index 2c66b098e7fd64e1293a625d4f7584b8c161c588..901058ec4bb4bb7fb22a99e2d8e0e74a502419b2 100644 (file)
@@ -464,6 +464,22 @@ def test_cancel_conn_nonblocking(pgconn):
         assert cancel_conn.status == pq.ConnStatus.OK
 
 
+@pytest.mark.libpq(">= 17")
+def test_cancel_conn_finished(pgconn):
+    cancel_conn = pgconn.cancel_conn()
+    cancel_conn.reset()
+    cancel_conn.finish()
+    with pytest.raises(psycopg.OperationalError):
+        cancel_conn.start()
+    with pytest.raises(psycopg.OperationalError):
+        cancel_conn.blocking()
+    with pytest.raises(psycopg.OperationalError):
+        cancel_conn.poll()
+    with pytest.raises(psycopg.OperationalError):
+        cancel_conn.reset()
+    assert cancel_conn.error_message.strip() == "connection pointer is NULL"
+
+
 def test_cancel(pgconn):
     cancel = pgconn.get_cancel()
     cancel.cancel()