Projects
openEuler:Roll:Everything:RVA20
python-waitress
_service:obs_scm:backport-CVE-2024-49769.patch
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:obs_scm:backport-CVE-2024-49769.patch of Package python-waitress
From 03cc640fe7106902899f82115c26e37002bca7f1 Mon Sep 17 00:00:00 2001 From: Delta Regeer <bertjw@regeer.org> Date: Sun, 3 Mar 2024 16:15:51 -0700 Subject: [PATCH 1/6] HTTPChannel is always created from accept, explicitly set self.connected to True --- src/waitress/channel.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/waitress/channel.py b/src/waitress/channel.py index eb59dd3..ea019d3 100644 --- a/src/waitress/channel.py +++ b/src/waitress/channel.py @@ -67,8 +67,7 @@ class HTTPChannel(wasyncore.dispatcher): self.outbuf_lock = threading.Condition() wasyncore.dispatcher.__init__(self, sock, map=map) - - # Don't let wasyncore.dispatcher throttle self.addr on us. + self.connected = True self.addr = addr self.requests = [] -- Gitee From 840aebce1c4c1bfd9036f402c1f5d5a4d2f4a1c2 Mon Sep 17 00:00:00 2001 From: Delta Regeer <bertjw@regeer.org> Date: Sun, 3 Mar 2024 16:16:48 -0700 Subject: [PATCH 2/6] Assume socket is not connected when passed to wasyncore.dispatcher No longer call getpeername() on the remote socket either, as it is not necessary for any of the places where waitress requires that self.addr in a subclass of the dispatcher needs it. This removes a race condition when setting up a HTTPChannel where we accepted the socket, and know the remote address, yet call getpeername() again which would have the unintended side effect of potentially setting self.connected to False because the remote has already shut down part of the socket. This issue was uncovered in #418, where the server would go into a hard loop because self.connected was used in various parts of the code base. --- src/waitress/wasyncore.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/waitress/wasyncore.py b/src/waitress/wasyncore.py index 8615557..9794308 100644 --- a/src/waitress/wasyncore.py +++ b/src/waitress/wasyncore.py @@ -297,22 +297,6 @@ class dispatcher: # get a socket from a blocking source. sock.setblocking(0) self.set_socket(sock, map) - self.connected = True - # The constructor no longer requires that the socket - # passed be connected. - try: - self.addr = sock.getpeername() - except OSError as err: - if err.args[0] in (ENOTCONN, EINVAL): - # To handle the case where we got an unconnected - # socket. - self.connected = False - else: - # The socket is broken in some unknown way, alert - # the user and remove it from the map (to prevent - # polling of broken sockets). - self.del_channel(map) - raise else: self.socket = None -- Gitee From 86c680df4e4bdd40c78dec771cddcee059e802c4 Mon Sep 17 00:00:00 2001 From: Delta Regeer <bertjw@regeer.org> Date: Sun, 3 Mar 2024 16:23:33 -0700 Subject: [PATCH 3/6] Remove test for getpeername() --- tests/test_wasyncore.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/test_wasyncore.py b/tests/test_wasyncore.py index 55c4019..a8a480a 100644 --- a/tests/test_wasyncore.py +++ b/tests/test_wasyncore.py @@ -1454,17 +1454,6 @@ class Test_dispatcher(unittest.TestCase): return dispatcher(sock=sock, map=map) - def test_unexpected_getpeername_exc(self): - sock = dummysocket() - - def getpeername(): - raise OSError(errno.EBADF) - - map = {} - sock.getpeername = getpeername - self.assertRaises(socket.error, self._makeOne, sock=sock, map=map) - self.assertEqual(map, {}) - def test___repr__accepting(self): sock = dummysocket() map = {} -- Gitee From 8cba302b1ac08c2874ae179b2af2445e89311bac Mon Sep 17 00:00:00 2001 From: Delta Regeer <bertjw@regeer.org> Date: Sun, 3 Mar 2024 16:26:22 -0700 Subject: [PATCH 4/6] Don't exit handle_write early -- even if socket is not connected Calling handle_close() multiple times does not hurt anything, and is safe. --- src/waitress/channel.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/waitress/channel.py b/src/waitress/channel.py index ea019d3..3860ed5 100644 --- a/src/waitress/channel.py +++ b/src/waitress/channel.py @@ -91,13 +91,7 @@ class HTTPChannel(wasyncore.dispatcher): # Precondition: there's data in the out buffer to be sent, or # there's a pending will_close request - if not self.connected: - # we dont want to close the channel twice - - return - # try to flush any pending output - if not self.requests: # 1. There are no running tasks, so we don't need to try to lock # the outbuf before sending -- Gitee From 63678e652d912e67621580123c603e37c319d8c4 Mon Sep 17 00:00:00 2001 From: Delta Regeer <bertjw@regeer.org> Date: Sun, 3 Mar 2024 16:35:39 -0700 Subject: [PATCH 5/6] Remove code not used by waitress from vendored asyncore --- src/waitress/wasyncore.py | 45 ------------------ tests/test_wasyncore.py | 96 ++++++++------------------------------- 2 files changed, 18 insertions(+), 123 deletions(-) diff --git a/src/waitress/wasyncore.py b/src/waitress/wasyncore.py index 9794308..49829f9 100644 --- a/src/waitress/wasyncore.py +++ b/src/waitress/wasyncore.py @@ -378,23 +378,6 @@ class dispatcher: self.addr = addr return self.socket.bind(addr) - def connect(self, address): - self.connected = False - self.connecting = True - err = self.socket.connect_ex(address) - if ( - err in (EINPROGRESS, EALREADY, EWOULDBLOCK) - or err == EINVAL - and os.name == "nt" - ): # pragma: no cover - self.addr = address - return - if err in (0, EISCONN): - self.addr = address - self.handle_connect_event() - else: - raise OSError(err, errorcode[err]) - def accept(self): # XXX can return either an address pair or None try: @@ -556,34 +539,6 @@ class dispatcher: self.close() -# --------------------------------------------------------------------------- -# adds simple buffered output capability, useful for simple clients. -# [for more sophisticated usage use asynchat.async_chat] -# --------------------------------------------------------------------------- - - -class dispatcher_with_send(dispatcher): - def __init__(self, sock=None, map=None): - dispatcher.__init__(self, sock, map) - self.out_buffer = b"" - - def initiate_send(self): - num_sent = 0 - num_sent = dispatcher.send(self, self.out_buffer[:65536]) - self.out_buffer = self.out_buffer[num_sent:] - - handle_write = initiate_send - - def writable(self): - return (not self.connected) or len(self.out_buffer) - - def send(self, data): - if self.debug: # pragma: no cover - self.log_info("sending %s" % repr(data)) - self.out_buffer = self.out_buffer + data - self.initiate_send() - - def close_all(map=None, ignore_all=False): if map is None: # pragma: no cover map = socket_map diff --git a/tests/test_wasyncore.py b/tests/test_wasyncore.py index a8a480a..20f68f6 100644 --- a/tests/test_wasyncore.py +++ b/tests/test_wasyncore.py @@ -1,6 +1,7 @@ import _thread as thread import contextlib import errno +from errno import EALREADY, EINPROGRESS, EINVAL, EISCONN, EWOULDBLOCK, errorcode import functools import gc from io import BytesIO @@ -641,62 +642,6 @@ class DispatcherTests(unittest.TestCase): self.assertTrue(err != "") -class dispatcherwithsend_noread(asyncore.dispatcher_with_send): # pragma: no cover - def readable(self): - return False - - def handle_connect(self): - pass - - -class DispatcherWithSendTests(unittest.TestCase): - def setUp(self): - pass - - def tearDown(self): - asyncore.close_all() - - @reap_threads - def test_send(self): - evt = threading.Event() - sock = socket.socket() - sock.settimeout(3) - port = bind_port(sock) - - cap = BytesIO() - args = (evt, cap, sock) - t = threading.Thread(target=capture_server, args=args) - t.start() - try: - # wait a little longer for the server to initialize (it sometimes - # refuses connections on slow machines without this wait) - time.sleep(0.2) - - data = b"Suppose there isn't a 16-ton weight?" - d = dispatcherwithsend_noread() - d.create_socket() - d.connect((HOST, port)) - - # give time for socket to connect - time.sleep(0.1) - - d.send(data) - d.send(data) - d.send(b"\n") - - n = 1000 - - while d.out_buffer and n > 0: # pragma: no cover - asyncore.poll() - n -= 1 - - evt.wait() - - self.assertEqual(cap.getvalue(), data * 2) - finally: - join_thread(t, timeout=TIMEOUT) - - @unittest.skipUnless( hasattr(asyncore, "file_wrapper"), "asyncore.file_wrapper required" ) @@ -839,6 +784,23 @@ class BaseClient(BaseTestHandler): self.create_socket(family) self.connect(address) + def connect(self, address): + self.connected = False + self.connecting = True + err = self.socket.connect_ex(address) + if ( + err in (EINPROGRESS, EALREADY, EWOULDBLOCK) + or err == EINVAL + and os.name == "nt" + ): # pragma: no cover + self.addr = address + return + if err in (0, EISCONN): + self.addr = address + self.handle_connect_event() + else: + raise OSError(err, errorcode[err]) + def handle_connect(self): pass @@ -1489,13 +1451,6 @@ class Test_dispatcher(unittest.TestCase): inst.set_reuse_addr() self.assertTrue(sock.errored) - def test_connect_raise_socket_error(self): - sock = dummysocket() - map = {} - sock.connect_ex = lambda *arg: 1 - inst = self._makeOne(sock=sock, map=map) - self.assertRaises(socket.error, inst.connect, 0) - def test_accept_raise_TypeError(self): sock = dummysocket() map = {} @@ -1664,21 +1619,6 @@ class Test_dispatcher(unittest.TestCase): self.assertTrue(sock.closed) -class Test_dispatcher_with_send(unittest.TestCase): - def _makeOne(self, sock=None, map=None): - from waitress.wasyncore import dispatcher_with_send - - return dispatcher_with_send(sock=sock, map=map) - - def test_writable(self): - sock = dummysocket() - map = {} - inst = self._makeOne(sock=sock, map=map) - inst.out_buffer = b"123" - inst.connected = True - self.assertTrue(inst.writable()) - - class Test_close_all(unittest.TestCase): def _callFUT(self, map=None, ignore_all=False): from waitress.wasyncore import close_all -- Gitee From 9d99c89ae4aa8449313eea210a5ec9f3994a87b2 Mon Sep 17 00:00:00 2001 From: Delta Regeer <bertjw@regeer.org> Date: Sun, 3 Mar 2024 16:37:12 -0700 Subject: [PATCH 6/6] When closing the socket, set it to None This avoids calling close() twice on the same socket if self.close() or self.handle_close() is called multiple times --- src/waitress/wasyncore.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/waitress/wasyncore.py b/src/waitress/wasyncore.py index 49829f9..f42ee37 100644 --- a/src/waitress/wasyncore.py +++ b/src/waitress/wasyncore.py @@ -436,6 +436,8 @@ class dispatcher: if why.args[0] not in (ENOTCONN, EBADF): raise + self.socket = None + # log and log_info may be overridden to provide more sophisticated # logging and warning methods. In general, log is for 'hit' logging # and 'log_info' is for informational, warning and error logging. @@ -486,7 +488,11 @@ class dispatcher: # handle_expt_event() is called if there might be an error on the # socket, or if there is OOB data # check for the error condition first - err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + err = ( + self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + if self.socket is not None + else 1 + ) if err != 0: # we can get here when select.select() says that there is an # exceptional condition on the socket -- Gitee
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2