public inbox for buildroot@busybox.net
 help / color / mirror / Atom feed
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

      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