diff --git a/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-111064.patch b/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-111064.patch
new file mode 100644
index 0000000000..9962a22ecb
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-111064.patch
@@ -0,0 +1,285 @@
+From be846822b4b3a5a54b6163e794b42dc06fba54a3 Mon Sep 17 00:00:00 2001
+From: Sam Bull <git@sambull.org>
+Date: Tue, 29 Sep 2026 00:08:22 +0100
+Subject: [PATCH] Close idle connections (#13681) (#13838)
+
+(cherry picked from commit e07684ef9056183bbefba1ae3f717a23f243631e)
+
+CVE: CVE-2026-111064
+Upstream-Status: Backport [https://github.com/aio-libs/aiohttp/commit/be846822b4b3a5a54b6163e794b42dc06fba54a3]
+
+Backport Changes:
+- Retain aiohttp 3.13.5's documented keepalive_timeout=3630 default
+  instead of changing it to the newer branch's 75-second default.
+- Import Any and AiohttpServer from existing modules for the
+  upstream server and WebSocket regression tests.
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ CHANGES/13681.bugfix.rst               |   1 +
+ aiohttp/web_protocol.py                |  12 ++-
+ docs/web_reference.rst                 |   9 ++
+ tests/test_web_server.py               | 118 ++++++++++++++++++++++++-
+ tests/test_web_websocket_functional.py |  34 ++++++-
+ 5 files changed, 167 insertions(+), 7 deletions(-)
+ create mode 100644 CHANGES/13681.bugfix.rst
+
+diff --git a/CHANGES/13681.bugfix.rst b/CHANGES/13681.bugfix.rst
+new file mode 100644
+index 0000000..e39b73a
+--- /dev/null
++++ b/CHANGES/13681.bugfix.rst
+@@ -0,0 +1 @@
++Fixed idle connections not being closed if no request was received -- by :user:`Dreamsorcerer`.
+diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py
+index d9843f9..c04aa10 100644
+--- a/aiohttp/web_protocol.py
++++ b/aiohttp/web_protocol.py
+@@ -117,8 +117,7 @@ class RequestHandler(BaseProtocol):
+     status line, bad headers or incomplete payload. If any error occurs,
+     connection gets closed.
+ 
+-    keepalive_timeout -- number of seconds before closing
+-                         keep-alive connection
++    keepalive_timeout -- number of seconds before closing an idle connection.
+ 
+     tcp_keepalive -- TCP keep-alive is on, default is on
+ 
+@@ -215,7 +214,7 @@ class RequestHandler(BaseProtocol):
+ 
+         # _request_count is the number of requests processed with the same connection.
+         self._request_count = 0
+-        self._keepalive = False
++        self._keepalive = True
+         self._current_request: Optional[BaseRequest] = None
+         self._manager: Optional[Server] = manager
+         self._request_handler: Optional[_RequestHandler] = manager.request_handler
+@@ -367,6 +366,13 @@ class RequestHandler(BaseProtocol):
+         self._manager.connection_made(self, real_transport)
+ 
+         loop = self._loop
++        # Need to enable keepalive timeout at start of connection, as there's no other
++        # protection against a dead connection that doesn't send a request at all.
++        if self._keepalive_timeout > 0:
++            close_time = loop.time() + self._keepalive_timeout
++            self._next_keepalive_close_time = close_time
++            self._keepalive_handle = loop.call_at(close_time, self._process_keepalive)
++
+         if sys.version_info >= (3, 12):
+             task = asyncio.Task(self.start(), loop=loop, eager_start=True)
+         else:
+diff --git a/docs/web_reference.rst b/docs/web_reference.rst
+index 83a9c7f..ddb43e8 100644
+--- a/docs/web_reference.rst
++++ b/docs/web_reference.rst
+@@ -3135,6 +3135,10 @@ Utilities
+    :param float keepalive_timeout: a delay before a TCP connection is
+                                    closed after a HTTP request. The delay
+                                    allows for reuse of a TCP connection.
++                                   The same delay bounds how long a newly
++                                   accepted connection may take to deliver
++                                   its first complete request; connections
++                                   that stay idle or incomplete are closed.
+ 
+                                    When deployed behind a reverse proxy
+                                    it's important for this value to be
+@@ -3144,6 +3148,11 @@ Utilities
+ 
+       .. versionadded:: 3.8
+ 
++      .. versionchanged:: 3.14.4
++
++         The timeout is now also applied while waiting for the first
++         request, closing connections that never send a complete request.
++
+    :param ssl_context: :class:`ssl.SSLContext` for HTTPS server,
+                        ``None`` for HTTP connection.
+ 
+diff --git a/tests/test_web_server.py b/tests/test_web_server.py
+index 09b7d0b..7595aec 100644
+--- a/tests/test_web_server.py
++++ b/tests/test_web_server.py
+@@ -1,7 +1,7 @@
+ import asyncio
+ import socket
+ from contextlib import suppress
+-from typing import NoReturn
++from typing import Any, NoReturn
+ from unittest import mock
+ 
+ import pytest
+@@ -417,3 +417,119 @@ async def test_no_handler_cancellation(unused_port_socket: socket.socket) -> Non
+         assert done_event.is_set()
+     finally:
+         await asyncio.gather(runner.shutdown(), site.stop())
++
++
++async def test_idle_connection_closed_before_first_request(
++    aiohttp_raw_server: AiohttpRawServer,
++) -> None:
++    """A connection that never sends a request must not be held open forever."""
++
++    async def handler(request: web.BaseRequest) -> web.Response:
++        assert False
++
++    server = await aiohttp_raw_server(handler, keepalive_timeout=0.2)
++
++    reader, writer = await asyncio.open_connection(server.host, server.port)
++    try:
++        # The server must close the connection once keepalive_timeout
++        # expires without a complete request having arrived.
++        assert await asyncio.wait_for(reader.read(), timeout=5) == b""
++    finally:
++        writer.close()
++        await writer.wait_closed()
++
++
++async def test_trickled_headers_closed_at_first_request_deadline(
++    aiohttp_raw_server: AiohttpRawServer, monkeypatch: pytest.MonkeyPatch
++) -> None:
++    """Partial header bytes must not extend the first-request deadline."""
++
++    async def handler(request: web.BaseRequest) -> web.Response:
++        assert False
++
++    server = await aiohttp_raw_server(handler, keepalive_timeout=0.4)
++    runner = server.runner
++    assert runner is not None
++    server_impl = runner.server
++    assert server_impl is not None
++
++    disconnected = asyncio.Event()
++    original_connection_lost = server_impl.connection_lost
++
++    def connection_lost(protocol: Any, exc: BaseException | None = None) -> None:
++        original_connection_lost(protocol, exc)
++        disconnected.set()
++
++    monkeypatch.setattr(server_impl, "connection_lost", connection_lost)
++
++    _, writer = await asyncio.open_connection(server.host, server.port)
++
++    async def trickle_until_closed() -> None:
++        writer.write(b"GET / HTTP/1.1\r\nHost: example.com\r\nX-Slow: ")
++        # asyncio's drain() raises a ConnectionError once the connection is
++        # lost; uvloop's write() raises RuntimeError on the closed transport.
++        with pytest.raises((ConnectionError, RuntimeError)):
++            while True:
++                await writer.drain()
++                await asyncio.sleep(0.05)
++                writer.write(b"a")
++
++    try:
++        # The deadline must fire despite the steady trickle of bytes.
++        await asyncio.wait_for(trickle_until_closed(), timeout=5)
++        await asyncio.wait_for(disconnected.wait(), timeout=5)
++        assert not server_impl.connections
++        assert server_impl.requests_count == 0
++    finally:
++        writer.close()
++        with suppress(ConnectionError, RuntimeError):
++            await writer.wait_closed()
++
++
++async def test_keepalive_timeout_zero_no_first_request_deadline(
++    aiohttp_raw_server: AiohttpRawServer,
++) -> None:
++    """keepalive_timeout=0 must not impose a deadline on the first request."""
++
++    async def handler(request: web.BaseRequest) -> web.Response:
++        return web.Response(text="ok")
++
++    server = await aiohttp_raw_server(handler, keepalive_timeout=0)
++
++    reader, writer = await asyncio.open_connection(server.host, server.port)
++    try:
++        # A zero timeout must not arm a deadline that fires at accept time:
++        # the connection has to survive an idle pause and serve the request.
++        await asyncio.sleep(0.1)
++        writer.write(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
++        await writer.drain()
++        head = await asyncio.wait_for(reader.readuntil(b"\r\n\r\n"), timeout=5)
++        assert head.startswith(b"HTTP/1.1 200 ")
++        # Between requests a zero timeout still closes the idle connection
++        # right after the response, so the body is followed by EOF.
++        assert await asyncio.wait_for(reader.read(), timeout=5) == b"ok"
++    finally:
++        writer.close()
++        await writer.wait_closed()
++
++
++async def test_handler_slower_than_first_request_deadline(
++    aiohttp_raw_server: AiohttpRawServer,
++) -> None:
++    """A parsed request being handled is not subject to the idle deadline."""
++
++    async def handler(request: web.BaseRequest) -> web.Response:
++        await asyncio.sleep(0.4)  # Longer than keepalive_timeout.
++        return web.Response(text="ok")
++
++    server = await aiohttp_raw_server(handler, keepalive_timeout=0.2)
++
++    reader, writer = await asyncio.open_connection(server.host, server.port)
++    try:
++        writer.write(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
++        await writer.drain()
++        head = await asyncio.wait_for(reader.readuntil(b"\r\n\r\n"), timeout=5)
++        assert head.startswith(b"HTTP/1.1 200 ")
++    finally:
++        writer.close()
++        await writer.wait_closed()
+diff --git a/tests/test_web_websocket_functional.py b/tests/test_web_websocket_functional.py
+index f7f5c31..871987e 100644
+--- a/tests/test_web_websocket_functional.py
++++ b/tests/test_web_websocket_functional.py
+@@ -12,7 +12,7 @@ import pytest
+ import aiohttp
+ from aiohttp import web
+ from aiohttp.http import WSCloseCode, WSMsgType
+-from aiohttp.pytest_plugin import AiohttpClient
++from aiohttp.pytest_plugin import AiohttpClient, AiohttpServer
+ 
+ 
+ async def test_websocket_can_prepare(loop, aiohttp_client) -> None:
+@@ -953,9 +953,37 @@ async def test_websocket_disable_keepalive(loop, aiohttp_client) -> None:
+     assert data == "OK"
+ 
+ 
+-async def test_receive_str_nonstring(
+-    loop: asyncio.AbstractEventLoop, aiohttp_client: AiohttpClient
++async def test_websocket_disable_keepalive_no_armed_handle(
++    aiohttp_server: AiohttpServer, aiohttp_client: AiohttpClient
+ ) -> None:
++    """keep_alive(False) when no keepalive timer was armed (keepalive_timeout=0)."""
++
++    async def handler(request: web.Request) -> web.StreamResponse:
++        ws = web.WebSocketResponse()
++        assert ws.can_prepare(request)
++        # A zero timeout arms no deadline at connection_made().
++        assert request.protocol._keepalive_handle is None
++        await ws.prepare(request)
++        assert not request.protocol._keepalive
++        assert request.protocol._keepalive_handle is None
++
++        await ws.send_str("OK")
++        await ws.close()
++        return ws
++
++    app = web.Application()
++    app.router.add_route("GET", "/", handler)
++    server = await aiohttp_server(app, keepalive_timeout=0)
++    client = await aiohttp_client(server)  # type: ignore[var-annotated]
++
++    ws = await client.ws_connect("/")
++    data = await ws.receive_str()
++    assert data == "OK"
++
++    await ws.receive()  # Handle close
++
++
++async def test_receive_str_nonstring(aiohttp_client: AiohttpClient) -> None:
+     async def handler(request: web.Request) -> web.WebSocketResponse:
+         ws = web.WebSocketResponse()
+         assert ws.can_prepare(request)
+-- 
+2.35.6
diff --git a/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb b/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb
index c6167ef607..98fd4bdba3 100644
--- a/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb
+++ b/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb
@@ -31,6 +31,7 @@ SRC_URI += " \
     file://CVE-2026-111748_p2.patch \
     file://CVE-2026-111063_p1.patch \
     file://CVE-2026-111063_p2.patch \
+    file://CVE-2026-111064.patch \
 "
 
 CVE_PRODUCT = "aiohttp:aiohttp"
