From: Thomas Perale via buildroot <buildroot@buildroot.org>
To: Thomas Perale <thomas.perale@mind.be>
Cc: buildroot@buildroot.org
Subject: Re: [Buildroot] [2025.02.x, PATCH 1/2] package/python-tornado: patch CVE-2025-67724, CVE-2025-67725, CVE-2025-67726
Date: Fri, 27 Mar 2026 11:02:17 +0100 [thread overview]
Message-ID: <20260327100219.45821-1-thomas.perale@mind.be> (raw)
In-Reply-To: <20260316151301.268215-1-thomas.perale@mind.be>
In reply of:
> Fixes the following vulnerabilities:
>
> - CVE-2025-67724:
> Tornado is a Python web framework and asynchronous networking library.
> In versions 6.5.2 and below, the supplied reason phrase is used
> unescaped in HTTP headers (where it could be used for header
> injection) or in HTML in the default error page (where it could be
> used for XSS) and can be exploited by passing untrusted or malicious
> data into the reason argument. Used by both RequestHandler.set_status
> and tornado.web.HTTPError, the argument is designed to allow
> applications to pass custom "reason" phrases (the "Not Found" in
> HTTP/1.1 404 Not Found) to the HTTP status line (mainly for non-
> standard status codes). This issue is fixed in version 6.5.3.
>
> For more information, see:
> - https://www.cve.org/CVERecord?id=CVE-2025-67724
> - https://github.com/tornadoweb/tornado/commit/9c163aebeaad9e6e7d28bac1f33580eb00b0e421
>
> - CVE-2025-67725:
> Tornado is a Python web framework and asynchronous networking library.
> In versions 6.5.2 and below, a single maliciously crafted HTTP request
> can block the server's event loop for an extended period, caused by
> the HTTPHeaders.add method. The function accumulates values using
> string concatenation when the same header name is repeated, causing a
> Denial of Service (DoS). Due to Python string immutability, each
> concatenation copies the entire string, resulting in O(n²) time
> complexity. The severity can vary from high if max_header_size has
> been increased from its default, to low if it has its default value of
> 64KB. This issue is fixed in version 6.5.3.
>
> For more information, see:
> - https://www.cve.org/CVERecord?id=CVE-2025-67725
> - https://github.com/tornadoweb/tornado/commit/771472cfdaeebc0d89a9cc46e249f8891a6b29cd
>
> - CVE-2025-67726:
> Tornado is a Python web framework and asynchronous networking library.
> Versions 6.5.2 and below use an inefficient algorithm when parsing
> parameters for HTTP header values, potentially causing a DoS. The
> _parseparam function in httputil.py is used to parse specific HTTP
> header values, such as those in multipart/form-data and repeatedly
> calls string.count() within a nested loop while processing quoted
> semicolons. If an attacker sends a request with a large number of
> maliciously crafted parameters in a Content-Disposition header, the
> server's CPU usage increases quadratically (O(n²)) during parsing. Due
> to Tornado's single event loop architecture, a single malicious
> request can cause the entire server to become unresponsive for an
> extended period. This issue is fixed in version 6.5.3.
>
> For more information, see:
> - https://www.cve.org/CVERecord?id=CVE-2025-67726
> - https://github.com/tornadoweb/tornado/commit/771472cfdaeebc0d89a9cc46e249f8891a6b29cd
>
> Signed-off-by: Thomas Perale <thomas.perale@mind.be>
Applied to 2025.02.x. Thanks
> ---
> ...-against-invalid-HTTP-reason-phrases.patch | 83 +++++++++++++++++++
> ...Fix-quadratic-behavior-in-parseparam.patch | 65 +++++++++++++++
> package/python-tornado/python-tornado.mk | 6 ++
> 3 files changed, 154 insertions(+)
> create mode 100644 package/python-tornado/0002-web-Harden-against-invalid-HTTP-reason-phrases.patch
> create mode 100644 package/python-tornado/0003-httputil-Fix-quadratic-behavior-in-parseparam.patch
>
> diff --git a/package/python-tornado/0002-web-Harden-against-invalid-HTTP-reason-phrases.patch b/package/python-tornado/0002-web-Harden-against-invalid-HTTP-reason-phrases.patch
> new file mode 100644
> index 0000000000..ac3c9806a2
> --- /dev/null
> +++ b/package/python-tornado/0002-web-Harden-against-invalid-HTTP-reason-phrases.patch
> @@ -0,0 +1,83 @@
> +From 9c163aebeaad9e6e7d28bac1f33580eb00b0e421 Mon Sep 17 00:00:00 2001
> +From: Ben Darnell <ben@bendarnell.com>
> +Date: Wed, 10 Dec 2025 15:15:25 -0500
> +Subject: [PATCH] web: Harden against invalid HTTP reason phrases
> +
> +We allow applications to set custom reason phrases for the HTTP status
> +line (to support custom status codes), but if this were exposed to
> +untrusted data it could be exploited in various ways. This commit
> +guards against invalid reason phrases in both HTTP headers and in
> +error pages.
> +
> +CVE: CVE-2025-67724
> +Upstream: https://github.com/tornadoweb/tornado/commit/9c163aebeaad9e6e7d28bac1f33580eb00b0e421
> +Signed-off-by: Thomas Perale <thomas.perale@mind.be>
> +---
> + tornado/web.py | 25 +++++++++++++++++++------
> + 1 files changed, 19 insertions(+), 6 deletions(-)
> +
> +diff --git a/tornado/web.py b/tornado/web.py
> +index 2f702d6480..2351afdbe2 100644
> +--- a/tornado/web.py
> ++++ b/tornado/web.py
> +@@ -359,8 +359,10 @@ def set_status(self, status_code: int, reason: Optional[str] = None) -> None:
> +
> + :arg int status_code: Response status code.
> + :arg str reason: Human-readable reason phrase describing the status
> +- code. If ``None``, it will be filled in from
> +- `http.client.responses` or "Unknown".
> ++ code (for example, the "Not Found" in ``HTTP/1.1 404 Not Found``).
> ++ Normally determined automatically from `http.client.responses`; this
> ++ argument should only be used if you need to use a non-standard
> ++ status code.
> +
> + .. versionchanged:: 5.0
> +
> +@@ -369,6 +371,14 @@ def set_status(self, status_code: int, reason: Optional[str] = None) -> None:
> + """
> + self._status_code = status_code
> + if reason is not None:
> ++ if "<" in reason or not httputil._ABNF.reason_phrase.fullmatch(reason):
> ++ # Logically this would be better as an exception, but this method
> ++ # is called on error-handling paths that would need some refactoring
> ++ # to tolerate internal errors cleanly.
> ++ #
> ++ # The check for "<" is a defense-in-depth against XSS attacks (we also
> ++ # escape the reason when rendering error pages).
> ++ reason = "Unknown"
> + self._reason = escape.native_str(reason)
> + else:
> + self._reason = httputil.responses.get(status_code, "Unknown")
> +@@ -1345,7 +1355,8 @@ def send_error(self, status_code: int = 500, **kwargs: Any) -> None:
> + reason = exception.reason
> + self.set_status(status_code, reason=reason)
> + try:
> +- self.write_error(status_code, **kwargs)
> ++ if status_code != 304:
> ++ self.write_error(status_code, **kwargs)
> + except Exception:
> + app_log.error("Uncaught exception in write_error", exc_info=True)
> + if not self._finished:
> +@@ -1373,7 +1384,7 @@ def write_error(self, status_code: int, **kwargs: Any) -> None:
> + self.finish(
> + "<html><title>%(code)d: %(message)s</title>"
> + "<body>%(code)d: %(message)s</body></html>"
> +- % {"code": status_code, "message": self._reason}
> ++ % {"code": status_code, "message": escape.xhtml_escape(self._reason)}
> + )
> +
> + @property
> +@@ -2520,9 +2531,11 @@ class HTTPError(Exception):
> + mode). May contain ``%s``-style placeholders, which will be filled
> + in with remaining positional parameters.
> + :arg str reason: Keyword-only argument. The HTTP "reason" phrase
> +- to pass in the status line along with ``status_code``. Normally
> ++ to pass in the status line along with ``status_code`` (for example,
> ++ the "Not Found" in ``HTTP/1.1 404 Not Found``). Normally
> + determined automatically from ``status_code``, but can be used
> +- to use a non-standard numeric code.
> ++ to use a non-standard numeric code. This is not a general-purpose
> ++ error message.
> + """
> +
> + def __init__(
> diff --git a/package/python-tornado/0003-httputil-Fix-quadratic-behavior-in-parseparam.patch b/package/python-tornado/0003-httputil-Fix-quadratic-behavior-in-parseparam.patch
> new file mode 100644
> index 0000000000..f1f4d5adbf
> --- /dev/null
> +++ b/package/python-tornado/0003-httputil-Fix-quadratic-behavior-in-parseparam.patch
> @@ -0,0 +1,65 @@
> +From 771472cfdaeebc0d89a9cc46e249f8891a6b29cd Mon Sep 17 00:00:00 2001
> +From: Ben Darnell <ben@bendarnell.com>
> +Date: Wed, 10 Dec 2025 10:55:02 -0500
> +Subject: [PATCH] httputil: Fix quadratic behavior in _parseparam
> +
> +Prior to this change, _parseparam had O(n^2) behavior when parsing
> +certain inputs, which could be a DoS vector. This change adapts
> +logic from the equivalent function in the python standard library
> +in https://github.com/python/cpython/pull/136072/files
> +
> +CVE: CVE-2025-67725
> +CVE: CVE-2025-67726
> +Upstream: https://github.com/tornadoweb/tornado/commit/771472cfdaeebc0d89a9cc46e249f8891a6b29cd
> +Signed-off-by: Thomas Perale <thomas.perale@mind.be>
> +---
> + tornado/httputil.py | 29 ++++++++++++++++++++++-------
> + tornado/test/httputil_test.py | 23 +++++++++++++++++++++++
> + 2 files changed, 45 insertions(+), 7 deletions(-)
> +
> +diff --git a/tornado/httputil.py b/tornado/httputil.py
> +index 1c48db414..7fa30975d 100644
> +--- a/tornado/httputil.py
> ++++ b/tornado/httputil.py
> +@@ -1094,19 +1094,34 @@ def parse_response_start_line(line: str) -> ResponseStartLine:
> + # It has also been modified to support valueless parameters as seen in
> + # websocket extension negotiations, and to support non-ascii values in
> + # RFC 2231/5987 format.
> ++#
> ++# _parseparam has been further modified with the logic from
> ++# https://github.com/python/cpython/pull/136072/files
> ++# to avoid quadratic behavior when parsing semicolons in quoted strings.
> ++#
> ++# TODO: See if we can switch to email.message.Message for this functionality.
> ++# This is the suggested replacement for the cgi.py module now that cgi has
> ++# been removed from recent versions of Python. We need to verify that
> ++# the email module is consistent with our existing behavior (and all relevant
> ++# RFCs for multipart/form-data) before making this change.
> +
> +
> + def _parseparam(s: str) -> Generator[str, None, None]:
> +- while s[:1] == ";":
> +- s = s[1:]
> +- end = s.find(";")
> +- while end > 0 and (s.count('"', 0, end) - s.count('\\"', 0, end)) % 2:
> +- end = s.find(";", end + 1)
> ++ start = 0
> ++ while s.find(";", start) == start:
> ++ start += 1
> ++ end = s.find(";", start)
> ++ ind, diff = start, 0
> ++ while end > 0:
> ++ diff += s.count('"', ind, end) - s.count('\\"', ind, end)
> ++ if diff % 2 == 0:
> ++ break
> ++ end, ind = ind, s.find(";", end + 1)
> + if end < 0:
> + end = len(s)
> +- f = s[:end]
> ++ f = s[start:end]
> + yield f.strip()
> +- s = s[end:]
> ++ start = end
> +
> +
> + def _parse_header(line: str) -> Tuple[str, Dict[str, str]]:
> diff --git a/package/python-tornado/python-tornado.mk b/package/python-tornado/python-tornado.mk
> index 0a33a57ed4..3e0b79443c 100644
> --- a/package/python-tornado/python-tornado.mk
> +++ b/package/python-tornado/python-tornado.mk
> @@ -16,4 +16,10 @@ PYTHON_TORNADO_SETUP_TYPE = setuptools
> # 0001-httputil-raise-errors-instead-of-logging-in.patch
> PYTHON_TORNADO_IGNORE_CVES += CVE-2025-47287
>
> +# 0002-web-Harden-against-invalid-HTTP-reason-phrases.patch
> +PYTHON_TORNADO_IGNORE_CVES += CVE-2025-67724
> +
> +# 0003-httputil-Fix-quadratic-behavior-in-parseparam.patch
> +PYTHON_TORNADO_IGNORE_CVES += CVE-2025-67725 CVE-2025-67726
> +
> $(eval $(python-package))
> --
> 2.53.0
>
> _______________________________________________
> buildroot mailing list
> buildroot@buildroot.org
> https://lists.buildroot.org/mailman/listinfo/buildroot
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot
prev parent reply other threads:[~2026-03-27 10:02 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-16 15:13 [Buildroot] [2025.02.x, PATCH 1/2] package/python-tornado: patch CVE-2025-67724, CVE-2025-67725, CVE-2025-67726 Thomas Perale via buildroot
2026-03-16 15:13 ` [Buildroot] [2025.02.x, PATCH 2/2] package/python-wheel: patch CVE-2026-24049 Thomas Perale via buildroot
2026-03-27 10:02 ` Thomas Perale via buildroot
2026-03-27 10:02 ` Thomas Perale via buildroot [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260327100219.45821-1-thomas.perale@mind.be \
--to=buildroot@buildroot.org \
--cc=thomas.perale@mind.be \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox