From: Denis Laxalde Date: Fri, 5 Apr 2024 08:47:47 +0000 (+0200) Subject: fix: prevent usage of finished PGcancelConn X-Git-Tag: 3.2.0~60^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ce02f59ca4058ef940dba8823525f0150d6dc2e5;p=thirdparty%2Fpsycopg.git fix: prevent usage of finished PGcancelConn Similarly to PGconn, PGcancelConn must not be used after .finish(); we raise an OperationalError to prevent segfault when the connection pointer is NULL. --- diff --git a/psycopg/psycopg/pq/pq_ctypes.py b/psycopg/psycopg/pq/pq_ctypes.py index a2fabbbab..ee3b51c19 100644 --- a/psycopg/psycopg/pq/pq_ctypes.py +++ b/psycopg/psycopg/pq/pq_ctypes.py @@ -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: """ diff --git a/psycopg_c/psycopg_c/pq/pgcancel.pyx b/psycopg_c/psycopg_c/pq/pgcancel.pyx index 2922fd765..ab64763fa 100644 --- a/psycopg_c/psycopg_c/pq/pgcancel.pyx +++ b/psycopg_c/psycopg_c/pq/pgcancel.pyx @@ -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): diff --git a/tests/pq/test_pgconn.py b/tests/pq/test_pgconn.py index 2c66b098e..901058ec4 100644 --- a/tests/pq/test_pgconn.py +++ b/tests/pq/test_pgconn.py @@ -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()