]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
fix(c): guard select() from high number file descriptors
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sat, 29 Oct 2022 10:27:10 +0000 (12:27 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 11 Dec 2022 20:04:31 +0000 (20:04 +0000)
The Python `select.select()` wrapper raises ValueError. Without a guard,
the C `wait_select()` segfaults. The C way to say you have been naughty.

On Windows this seems not only not needed, but it actually stops from creating
any connection at all.

psycopg_c/psycopg_c/_psycopg/waiting.pyx
tests/test_waiting.py

index be8aaee568b877cc1333f1c33b92e982bf5fc423..3dc068b9085de3573f7368bc8377f9cc94e3db6b 100644 (file)
@@ -31,6 +31,15 @@ select_impl(int fileno, int wait, float timeout)
     struct timeval tv, *tvptr;
     int select_rv;
 
+#ifdef MS_WINDOWS
+    if (fileno >= 1024) {
+        PyErr_SetString(
+            PyExc_ValueError,  /* same exception of Python's 'select.select()' */
+            "connection file descriptor out of range for 'select()'");
+        return -1;
+    }
+#endif
+
     FD_ZERO(&ifds);
     FD_ZERO(&ofds);
     FD_ZERO(&efds);
index acd1e95b139e336d5cd961224fc3d69c32beb785..72c258d7825f4ce78ae197c0f8288b8b7ebcbd80 100644 (file)
@@ -76,6 +76,35 @@ def test_wait_bad(pgconn, waitfn):
         waitfn(gen, pgconn.socket)
 
 
+@pytest.mark.slow
+@pytest.mark.parametrize("waitfn", waitfns)
+def test_wait_large_fd(dsn, waitfn):
+    files = []
+    try:
+        try:
+            for i in range(1100):
+                files.append(open(__file__))
+        except OSError:
+            pytest.skip("can't open the number of files needed for the test")
+
+        pgconn = psycopg.pq.PGconn.connect(dsn.encode())
+        try:
+            assert pgconn.socket > 1024
+            pgconn.send_query(b"select 1")
+            gen = generators.execute(pgconn)
+            if waitfn is waiting.wait_select and sys.platform != "win32":
+                with pytest.raises(ValueError):
+                    waitfn(gen, pgconn.socket)
+            else:
+                (res,) = waitfn(gen, pgconn.socket)
+                assert res.status == ExecStatus.TUPLES_OK
+        finally:
+            pgconn.finish()
+    finally:
+        for f in files:
+            f.close()
+
+
 @pytest.mark.parametrize("timeout", timeouts)
 @pytest.mark.asyncio
 async def test_wait_conn_async(dsn, timeout):