new file mode 100644
@@ -0,0 +1,27 @@
+From fdabcb31093507f50fcaeb46012ec8df8bf76359 Mon Sep 17 00:00:00 2001
+From: Delta Regeer <bertjw@regeer.org>
+Date: Sun, 3 Mar 2024 16:15:51 -0700
+Subject: [PATCH] HTTPChannel is always created from accept, explicitly set
+ self.connected to True
+
+CVE: CVE-2024-49769
+Upstream-Status: Backport [https://github.com/Pylons/waitress/commit/03cc640fe7106902899f82115c26e37002bca7f1]
+Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
+---
+ 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 756adce..cf19ef2 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 = []
+
new file mode 100644
@@ -0,0 +1,53 @@
+From 646d7bfa81185b961b4797965f5c7ff0e380bc5c Mon Sep 17 00:00:00 2001
+From: Delta Regeer <bertjw@regeer.org>
+Date: Sun, 3 Mar 2024 16:16:48 -0700
+Subject: [PATCH] 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.
+
+CVE: CVE-2024-49769
+Upstream-Status: Backport [https://github.com/Pylons/waitress/commit/840aebce1c4c1bfd9036f402c1f5d5a4d2f4a1c2]
+Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
+---
+ src/waitress/wasyncore.py | 16 ----------------
+ 1 file changed, 16 deletions(-)
+
+diff --git a/src/waitress/wasyncore.py b/src/waitress/wasyncore.py
+index b3459e0..b5ddce2 100644
+--- a/src/waitress/wasyncore.py
++++ b/src/waitress/wasyncore.py
+@@ -298,22 +298,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
+
new file mode 100644
@@ -0,0 +1,34 @@
+From 28377c0e0fdd8669fb250e69745caf1c27ba541b Mon Sep 17 00:00:00 2001
+From: Delta Regeer <bertjw@regeer.org>
+Date: Sun, 3 Mar 2024 16:23:33 -0700
+Subject: [PATCH] Remove test for getpeername()
+
+CVE: CVE-2024-49769
+Upstream-Status: Backport [https://github.com/Pylons/waitress/commit/86c680df4e4bdd40c78dec771cddcee059e802c4]
+Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
+---
+ tests/test_wasyncore.py | 11 -----------
+ 1 file changed, 11 deletions(-)
+
+diff --git a/tests/test_wasyncore.py b/tests/test_wasyncore.py
+index e833c7e..5f38bd9 100644
+--- a/tests/test_wasyncore.py
++++ b/tests/test_wasyncore.py
+@@ -1451,17 +1451,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 = {}
new file mode 100644
@@ -0,0 +1,34 @@
+From ee501847c38e21be0683ba81925472f219044a65 Mon Sep 17 00:00:00 2001
+From: Delta Regeer <bertjw@regeer.org>
+Date: Sun, 3 Mar 2024 16:26:22 -0700
+Subject: [PATCH] Don't exit handle_write early -- even if socket is not
+ connected
+
+Calling handle_close() multiple times does not hurt anything, and is
+safe.
+
+CVE: CVE-2024-49769
+Upstream-Status: Backport [https://github.com/Pylons/waitress/commit/8cba302b1ac08c2874ae179b2af2445e89311bac]
+Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
+---
+ src/waitress/channel.py | 6 ------
+ 1 file changed, 6 deletions(-)
+
+diff --git a/src/waitress/channel.py b/src/waitress/channel.py
+index cf19ef2..f4d9677 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
new file mode 100644
@@ -0,0 +1,211 @@
+From aa161b98cc787f266d8ef358f00fc5b2b3944157 Mon Sep 17 00:00:00 2001
+From: Delta Regeer <bertjw@regeer.org>
+Date: Sun, 3 Mar 2024 16:35:39 -0700
+Subject: [PATCH] Remove code not used by waitress from vendored asyncore
+
+CVE: CVE-2024-49769
+Upstream-Status: Backport [https://github.com/Pylons/waitress/commit/63678e652d912e67621580123c603e37c319d8c4]
+Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
+---
+ 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 b5ddce2..117f78a 100644
+--- a/src/waitress/wasyncore.py
++++ b/src/waitress/wasyncore.py
+@@ -379,23 +379,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:
+@@ -557,34 +540,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 5f38bd9..44b8e19 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
+
+@@ -1486,13 +1448,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 = {}
+@@ -1661,21 +1616,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
new file mode 100644
@@ -0,0 +1,41 @@
+From 4a5ce98ecaed785a14781700106d60c4072c9b87 Mon Sep 17 00:00:00 2001
+From: Delta Regeer <bertjw@regeer.org>
+Date: Sun, 3 Mar 2024 16:37:12 -0700
+Subject: [PATCH] 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
+
+CVE: CVE-2024-49769
+Upstream-Status: Backport [https://github.com/Pylons/waitress/commit/9d99c89ae4aa8449313eea210a5ec9f3994a87b2]
+Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
+---
+ 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 117f78a..f0cd23e 100644
+--- a/src/waitress/wasyncore.py
++++ b/src/waitress/wasyncore.py
+@@ -437,6 +437,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.
+@@ -487,7 +489,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
@@ -14,6 +14,12 @@ SRC_URI += "file://CVE-2024-49768-1.patch \
file://CVE-2024-49768-2.patch \
file://CVE-2024-49768-3.patch \
file://CVE-2024-49768-4.patch \
+ file://CVE-2024-49769-1.patch \
+ file://CVE-2024-49769-2.patch \
+ file://CVE-2024-49769-3.patch \
+ file://CVE-2024-49769-4.patch \
+ file://CVE-2024-49769-5.patch \
+ file://CVE-2024-49769-6.patch \
"
SRC_URI[sha256sum] = "780a4082c5fbc0fde6a2fcfe5e26e6efc1e8f425730863c04085769781f51eba"
Details: https://nvd.nist.gov/vuln/detail/CVE-2024-49769 Pick the patch that is referenced in the NVD report (which is a merge commit. The patches here are the individual patches from that merge). Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> --- .../python3-waitress/CVE-2024-49769-1.patch | 27 +++ .../python3-waitress/CVE-2024-49769-2.patch | 53 +++++ .../python3-waitress/CVE-2024-49769-3.patch | 34 +++ .../python3-waitress/CVE-2024-49769-4.patch | 34 +++ .../python3-waitress/CVE-2024-49769-5.patch | 211 ++++++++++++++++++ .../python3-waitress/CVE-2024-49769-6.patch | 41 ++++ .../python/python3-waitress_2.1.2.bb | 6 + 7 files changed, 406 insertions(+) create mode 100644 meta-python/recipes-devtools/python/python3-waitress/CVE-2024-49769-1.patch create mode 100644 meta-python/recipes-devtools/python/python3-waitress/CVE-2024-49769-2.patch create mode 100644 meta-python/recipes-devtools/python/python3-waitress/CVE-2024-49769-3.patch create mode 100644 meta-python/recipes-devtools/python/python3-waitress/CVE-2024-49769-4.patch create mode 100644 meta-python/recipes-devtools/python/python3-waitress/CVE-2024-49769-5.patch create mode 100644 meta-python/recipes-devtools/python/python3-waitress/CVE-2024-49769-6.patch