From: Daniele Varrazzo Date: Sat, 29 Oct 2022 10:27:10 +0000 (+0200) Subject: fix(c): guard select() from high number file descriptors X-Git-Tag: 3.1.5~7^2~6 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=291524c7f7d769fcb0f5cce2497e1d14e46465d9;p=thirdparty%2Fpsycopg.git fix(c): guard select() from high number file descriptors 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. --- diff --git a/psycopg_c/psycopg_c/_psycopg/waiting.pyx b/psycopg_c/psycopg_c/_psycopg/waiting.pyx index be8aaee56..3dc068b90 100644 --- a/psycopg_c/psycopg_c/_psycopg/waiting.pyx +++ b/psycopg_c/psycopg_c/_psycopg/waiting.pyx @@ -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); diff --git a/tests/test_waiting.py b/tests/test_waiting.py index acd1e95b1..72c258d78 100644 --- a/tests/test_waiting.py +++ b/tests/test_waiting.py @@ -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):