mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-09-02 04:20:18 +00:00
This patch applies the upstream fix as referenced in [2], using the commit shown in [1]. [1] https://github.com/aio-libs/aiohttp/commit/a762eda5242f6490d6ba667533193f8b473ad587 [2] https://github.com/advisories/GHSA-9x8q-7h8h-wcw9 Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
139 lines
5.1 KiB
Diff
139 lines
5.1 KiB
Diff
From 724ae4d652417c4490a71ea0b16b8804dad3004f Mon Sep 17 00:00:00 2001
|
|
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
|
|
Date: Sun, 7 Jun 2026 05:47:16 +0000
|
|
Subject: [PATCH] [PR #12831/1ac92dae backport][3.14] Payload close on
|
|
disconnect (#12843)
|
|
|
|
CVE: CVE-2026-54280
|
|
Upstream-Status: Backport [https://github.com/aio-libs/aiohttp/commit/a762eda5242f6490d6ba667533193f8b473ad587]
|
|
|
|
Co-authored-by: J. Nick Koston <nick@koston.org>
|
|
(cherry picked from commit a762eda5242f6490d6ba667533193f8b473ad587)
|
|
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
|
|
---
|
|
CHANGES/12831.bugfix.rst | 1 +
|
|
aiohttp/web_response.py | 6 ++--
|
|
tests/test_web_response.py | 73 +++++++++++++++++++++++++++++++++++++-
|
|
3 files changed, 77 insertions(+), 3 deletions(-)
|
|
create mode 100644 CHANGES/12831.bugfix.rst
|
|
|
|
diff --git a/CHANGES/12831.bugfix.rst b/CHANGES/12831.bugfix.rst
|
|
new file mode 100644
|
|
index 000000000..bf460ffcc
|
|
--- /dev/null
|
|
+++ b/CHANGES/12831.bugfix.rst
|
|
@@ -0,0 +1 @@
|
|
+Fixed :meth:`aiohttp.web.Response.write_eof` skipping ``Payload.close()`` when the body write was interrupted by an error or cancellation, for example when a client disconnects mid-response; the payload close hook now runs in a ``finally`` so a :class:`~aiohttp.payload.Payload` body always releases its resources -- by :user:`bdraco`.
|
|
diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py
|
|
index 364270e4d..cea5d4b45 100644
|
|
--- a/aiohttp/web_response.py
|
|
+++ b/aiohttp/web_response.py
|
|
@@ -779,8 +779,10 @@ class Response(StreamResponse):
|
|
if body is None or self._must_be_empty_body:
|
|
await super().write_eof()
|
|
elif isinstance(self._body, Payload):
|
|
- await self._body.write(self._payload_writer)
|
|
- await self._body.close()
|
|
+ try:
|
|
+ await self._body.write(self._payload_writer)
|
|
+ finally:
|
|
+ await self._body.close()
|
|
await super().write_eof()
|
|
else:
|
|
await super().write_eof(cast(bytes, body))
|
|
diff --git a/tests/test_web_response.py b/tests/test_web_response.py
|
|
index 5a4fb7e66..f094cd3d2 100644
|
|
--- a/tests/test_web_response.py
|
|
+++ b/tests/test_web_response.py
|
|
@@ -1,3 +1,4 @@
|
|
+import asyncio
|
|
import collections.abc
|
|
import datetime
|
|
import gzip
|
|
@@ -18,7 +19,7 @@ from aiohttp.abc import AbstractStreamWriter
|
|
from aiohttp.helpers import ETag
|
|
from aiohttp.http_writer import StreamWriter, _serialize_headers
|
|
from aiohttp.multipart import BodyPartReader, MultipartWriter
|
|
-from aiohttp.payload import BytesPayload, StringPayload
|
|
+from aiohttp.payload import BytesPayload, Payload, StringPayload
|
|
from aiohttp.test_utils import make_mocked_request
|
|
from aiohttp.web import ContentCoding, Response, StreamResponse, json_response
|
|
|
|
@@ -1370,6 +1371,76 @@ async def test_consecutive_write_eof() -> None:
|
|
writer.write_eof.assert_called_once_with(data)
|
|
|
|
|
|
+class _ClosingPayload(Payload):
|
|
+ """Payload test double that records whether close() ran."""
|
|
+
|
|
+ def __init__(self) -> None:
|
|
+ super().__init__(None)
|
|
+ self.close_called = False
|
|
+ self.started = asyncio.Event()
|
|
+ self.release = asyncio.Event()
|
|
+ self.fail = False
|
|
+
|
|
+ async def write(self, writer: AbstractStreamWriter) -> None:
|
|
+ self.started.set()
|
|
+ if self.fail:
|
|
+ raise ConnectionResetError("client gone")
|
|
+ await self.release.wait()
|
|
+
|
|
+ async def close(self) -> None:
|
|
+ self.close_called = True
|
|
+ await super().close()
|
|
+
|
|
+ def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str:
|
|
+ assert False
|
|
+
|
|
+
|
|
+async def test_write_eof_closes_payload_on_success() -> None:
|
|
+ writer = mock.create_autospec(AbstractStreamWriter, spec_set=True, instance=True)
|
|
+ req = make_request("GET", "/", writer=writer)
|
|
+ payload = _ClosingPayload()
|
|
+ payload.release.set()
|
|
+ resp = web.Response(body=payload)
|
|
+
|
|
+ await resp.prepare(req)
|
|
+ await resp.write_eof()
|
|
+
|
|
+ assert payload.close_called
|
|
+ assert writer.write_eof.called
|
|
+
|
|
+
|
|
+async def test_write_eof_closes_payload_on_write_error() -> None:
|
|
+ writer = mock.create_autospec(AbstractStreamWriter, spec_set=True, instance=True)
|
|
+ req = make_request("GET", "/", writer=writer)
|
|
+ payload = _ClosingPayload()
|
|
+ payload.fail = True
|
|
+ resp = web.Response(body=payload)
|
|
+
|
|
+ await resp.prepare(req)
|
|
+ with pytest.raises(ConnectionResetError):
|
|
+ await resp.write_eof()
|
|
+
|
|
+ assert payload.close_called
|
|
+ assert not writer.write_eof.called
|
|
+
|
|
+
|
|
+async def test_write_eof_closes_payload_on_cancel() -> None:
|
|
+ writer = mock.create_autospec(AbstractStreamWriter, spec_set=True, instance=True)
|
|
+ req = make_request("GET", "/", writer=writer)
|
|
+ payload = _ClosingPayload()
|
|
+ resp = web.Response(body=payload)
|
|
+
|
|
+ await resp.prepare(req)
|
|
+ task = asyncio.ensure_future(resp.write_eof())
|
|
+ await payload.started.wait()
|
|
+ task.cancel()
|
|
+ with pytest.raises(asyncio.CancelledError):
|
|
+ await task
|
|
+
|
|
+ assert payload.close_called
|
|
+ assert not writer.write_eof.called
|
|
+
|
|
+
|
|
def test_set_text_with_content_type() -> None:
|
|
resp = Response()
|
|
resp.content_type = "text/html"
|