From e51cc5be24098239f85d7f2c44a08d043fa8ac81 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 19 Nov 2024 09:08:42 +0100 Subject: [PATCH 1/3] gh-126876: Fix socket internal_select() for large timeout (GH-126968) If the timeout is larger than INT_MAX, replace it with INT_MAX, in the poll() code path. Add an unit test. (cherry picked from commit b3687ad454c4ac54c8599a10f3ace8a13ca48915) Co-authored-by: Victor Stinner --- Lib/test/test_socket.py | 27 +++++++++++++++++++++++++++ Modules/socketmodule.c | 5 ++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index fd328a74134bcf..cdcb79d89ba3df 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -5059,6 +5059,33 @@ def _testRecv(self): # send data: recv() will no longer block self.cli.sendall(MSG) + def testLargeTimeout(self): + # gh-126876: Check that a timeout larger than INT_MAX is replaced with + # INT_MAX in the poll() code path. The following assertion must not + # fail: assert(INT_MIN <= ms && ms <= INT_MAX). + large_timeout = _testcapi.INT_MAX + 1 + + # test recv() with large timeout + conn, addr = self.serv.accept() + self.addCleanup(conn.close) + try: + conn.settimeout(large_timeout) + except OverflowError: + # On Windows, settimeout() fails with OverflowError, whereas + # we want to test recv(). Just give up silently. + return + msg = conn.recv(len(MSG)) + + def _testLargeTimeout(self): + # test sendall() with large timeout + large_timeout = _testcapi.INT_MAX + 1 + self.cli.connect((HOST, self.port)) + try: + self.cli.settimeout(large_timeout) + except OverflowError: + return + self.cli.sendall(MSG) + class FileObjectClassTestCase(SocketConnectedTest): """Unit tests for the object returned by socket.makefile() diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 97248792c0f090..7f2ebba9884f98 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -816,7 +816,9 @@ internal_select(PySocketSockObject *s, int writing, _PyTime_t interval, /* s->sock_timeout is in seconds, timeout in ms */ ms = _PyTime_AsMilliseconds(interval, _PyTime_ROUND_CEILING); - assert(ms <= INT_MAX); + if (ms > INT_MAX) { + ms = INT_MAX; + } /* On some OSes, typically BSD-based ones, the timeout parameter of the poll() syscall, when negative, must be exactly INFTIM, where defined, @@ -828,6 +830,7 @@ internal_select(PySocketSockObject *s, int writing, _PyTime_t interval, ms = -1; #endif } + assert(INT_MIN <= ms && ms <= INT_MAX); Py_BEGIN_ALLOW_THREADS; n = poll(&pollfd, 1, (int)ms); From db50895f505def5bd15b13e253901d70720343e0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 2 Dec 2024 16:51:50 +0100 Subject: [PATCH 2/3] gh-126876: Fix test_socket.testLargeTimeout() for missing _testcapi (#127517) (cherry picked from commit c46acd3588864e97d0e0fe37a41aa5e94ac7af51) --- Lib/test/test_socket.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index cdcb79d89ba3df..1343a883fe2bce 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -5063,7 +5063,10 @@ def testLargeTimeout(self): # gh-126876: Check that a timeout larger than INT_MAX is replaced with # INT_MAX in the poll() code path. The following assertion must not # fail: assert(INT_MIN <= ms && ms <= INT_MAX). - large_timeout = _testcapi.INT_MAX + 1 + if _testcapi is not None: + large_timeout = _testcapi.INT_MAX + 1 + else: + large_timeout = 2147483648 # test recv() with large timeout conn, addr = self.serv.accept() @@ -5078,7 +5081,10 @@ def testLargeTimeout(self): def _testLargeTimeout(self): # test sendall() with large timeout - large_timeout = _testcapi.INT_MAX + 1 + if _testcapi is not None: + large_timeout = _testcapi.INT_MAX + 1 + else: + large_timeout = 2147483648 self.cli.connect((HOST, self.port)) try: self.cli.settimeout(large_timeout) From 6cec07ec3e55d0cf7035a19aeb5c3750260e1731 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 3 Dec 2024 14:56:01 +0100 Subject: [PATCH 3/3] Fix testLargeTimeout() --- Lib/test/test_socket.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 1343a883fe2bce..b40ad28f7a7dad 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -5059,14 +5059,13 @@ def _testRecv(self): # send data: recv() will no longer block self.cli.sendall(MSG) + @support.cpython_only def testLargeTimeout(self): # gh-126876: Check that a timeout larger than INT_MAX is replaced with # INT_MAX in the poll() code path. The following assertion must not # fail: assert(INT_MIN <= ms && ms <= INT_MAX). - if _testcapi is not None: - large_timeout = _testcapi.INT_MAX + 1 - else: - large_timeout = 2147483648 + import _testcapi + large_timeout = _testcapi.INT_MAX + 1 # test recv() with large timeout conn, addr = self.serv.accept() @@ -5081,10 +5080,8 @@ def testLargeTimeout(self): def _testLargeTimeout(self): # test sendall() with large timeout - if _testcapi is not None: - large_timeout = _testcapi.INT_MAX + 1 - else: - large_timeout = 2147483648 + import _testcapi + large_timeout = _testcapi.INT_MAX + 1 self.cli.connect((HOST, self.port)) try: self.cli.settimeout(large_timeout)