Package: release.debian.org Severity: normal User: release.debian....@packages.debian.org Usertags: unblock X-Debbugs-Cc: python-torn...@packages.debian.org Control: affects -1 + src:python-tornado
Please unblock package python-tornado [ Reason ] Previous uploading intended to fix CVE-2025-47287[0] issue, but unfortunately, this caused a timeout issue on riscv64, see #1106130[1]. [ Impact ] The CVE-2025-47287 is still only fixed in unstable due to preventing migration. [ Tests ] The package builds fine with test cases and pass autopkgtest. [ Risks ] Very low. This upload only increase timeout one riscv64 so almost no risk. Because the previous upload has not been migrated to testing, there may be risks, but security updates are more important. [ Checklist ] [x] all changes are documented in the d/changelog [x] I reviewed all changes and I approve them [x] attach debdiff against the package in testing I have attached two diff(s): python-tornado-testing.diff from 6.4.2-3 vs 6.4.2-1(testing) python-tornado_6.4.2-3.diff from 6.4.2-3 vs 6.4.2-2. unblock python-tornado/6.4.2-3 [0]: https://tracker.debian.org/news/1646589/accepted-python-tornado-642-2-source-into-unstable/ [1]: https://bugs.debian.org/1106130 -- Regards, -- Bo YU
diff -Nru python-tornado-6.4.2/debian/changelog python-tornado-6.4.2/debian/changelog --- python-tornado-6.4.2/debian/changelog 2024-11-29 21:09:50.000000000 +0800 +++ python-tornado-6.4.2/debian/changelog 2025-05-22 21:16:28.000000000 +0800 @@ -1,3 +1,18 @@ +python-tornado (6.4.2-3) unstable; urgency=medium + + * Team upload. + * Increase timeout on riscv64 to fix FTBFS issue. (Closes: #1106130) + + -- Bo YU <vi...@debian.org> Thu, 22 May 2025 21:16:28 +0800 + +python-tornado (6.4.2-2) unstable; urgency=medium + + * Team upload. + * CVE-2025-47287: httputil: Raise errors instead of logging in + multipart/form-data parsing (closes: #1105886). + + -- Colin Watson <cjwat...@debian.org> Sun, 18 May 2025 16:43:40 +0100 + python-tornado (6.4.2-1) unstable; urgency=medium * Team upload. diff -Nru python-tornado-6.4.2/debian/.gitignore python-tornado-6.4.2/debian/.gitignore --- python-tornado-6.4.2/debian/.gitignore 2024-11-29 21:09:50.000000000 +0800 +++ python-tornado-6.4.2/debian/.gitignore 1970-01-01 07:30:00.000000000 +0730 @@ -1 +0,0 @@ -/files diff -Nru python-tornado-6.4.2/debian/patches/CVE-2025-47287.patch python-tornado-6.4.2/debian/patches/CVE-2025-47287.patch --- python-tornado-6.4.2/debian/patches/CVE-2025-47287.patch 1970-01-01 07:30:00.000000000 +0730 +++ python-tornado-6.4.2/debian/patches/CVE-2025-47287.patch 2025-05-22 21:16:28.000000000 +0800 @@ -0,0 +1,231 @@ +From: Ben Darnell <b...@bendarnell.com> +Date: Thu, 8 May 2025 13:29:43 -0400 +Subject: httputil: Raise errors instead of logging in multipart/form-data + parsing + +We used to continue after logging an error, which allowed repeated +errors to spam the logs. The error raised here will still be logged, +but only once per request, consistent with other error handling in +Tornado. + +Origin: backport, https://github.com/tornadoweb/tornado/pull/3497 +Bug-Debian: https://bugs.debian.org/1105886 +Last-Update: 2025-05-18 +--- + tornado/httputil.py | 30 +++++++++++------------------- + tornado/test/httpserver_test.py | 4 ++-- + tornado/test/httputil_test.py | 13 ++++++++----- + tornado/web.py | 17 +++++++++++++---- + 4 files changed, 34 insertions(+), 30 deletions(-) + +diff --git a/tornado/httputil.py b/tornado/httputil.py +index ebdc805..090a977 100644 +--- a/tornado/httputil.py ++++ b/tornado/httputil.py +@@ -34,7 +34,6 @@ import unicodedata + from urllib.parse import urlencode, urlparse, urlunparse, parse_qsl + + from tornado.escape import native_str, parse_qs_bytes, utf8 +-from tornado.log import gen_log + from tornado.util import ObjectDict, unicode_type + + +@@ -762,25 +761,22 @@ def parse_body_arguments( + """ + if content_type.startswith("application/x-www-form-urlencoded"): + if headers and "Content-Encoding" in headers: +- gen_log.warning( +- "Unsupported Content-Encoding: %s", headers["Content-Encoding"] ++ raise HTTPInputError( ++ "Unsupported Content-Encoding: %s" % headers["Content-Encoding"] + ) +- return + try: + # real charset decoding will happen in RequestHandler.decode_argument() + uri_arguments = parse_qs_bytes(body, keep_blank_values=True) + except Exception as e: +- gen_log.warning("Invalid x-www-form-urlencoded body: %s", e) +- uri_arguments = {} ++ raise HTTPInputError("Invalid x-www-form-urlencoded body: %s" % e) from e + for name, values in uri_arguments.items(): + if values: + arguments.setdefault(name, []).extend(values) + elif content_type.startswith("multipart/form-data"): + if headers and "Content-Encoding" in headers: +- gen_log.warning( +- "Unsupported Content-Encoding: %s", headers["Content-Encoding"] ++ raise HTTPInputError( ++ "Unsupported Content-Encoding: %s" % headers["Content-Encoding"] + ) +- return + try: + fields = content_type.split(";") + for field in fields: +@@ -789,9 +785,9 @@ def parse_body_arguments( + parse_multipart_form_data(utf8(v), body, arguments, files) + break + else: +- raise ValueError("multipart boundary not found") ++ raise HTTPInputError("multipart boundary not found") + except Exception as e: +- gen_log.warning("Invalid multipart/form-data: %s", e) ++ raise HTTPInputError("Invalid multipart/form-data: %s" % e) from e + + + def parse_multipart_form_data( +@@ -820,26 +816,22 @@ def parse_multipart_form_data( + boundary = boundary[1:-1] + final_boundary_index = data.rfind(b"--" + boundary + b"--") + if final_boundary_index == -1: +- gen_log.warning("Invalid multipart/form-data: no final boundary") +- return ++ raise HTTPInputError("Invalid multipart/form-data: no final boundary found") + parts = data[:final_boundary_index].split(b"--" + boundary + b"\r\n") + for part in parts: + if not part: + continue + eoh = part.find(b"\r\n\r\n") + if eoh == -1: +- gen_log.warning("multipart/form-data missing headers") +- continue ++ raise HTTPInputError("multipart/form-data missing headers") + headers = HTTPHeaders.parse(part[:eoh].decode("utf-8")) + disp_header = headers.get("Content-Disposition", "") + disposition, disp_params = _parse_header(disp_header) + if disposition != "form-data" or not part.endswith(b"\r\n"): +- gen_log.warning("Invalid multipart/form-data") +- continue ++ raise HTTPInputError("Invalid multipart/form-data") + value = part[eoh + 4 : -2] + if not disp_params.get("name"): +- gen_log.warning("multipart/form-data value missing name") +- continue ++ raise HTTPInputError("multipart/form-data missing name") + name = disp_params["name"] + if disp_params.get("filename"): + ctype = headers.get("Content-Type", "application/unknown") +diff --git a/tornado/test/httpserver_test.py b/tornado/test/httpserver_test.py +index 0b29a39..5d5fb13 100644 +--- a/tornado/test/httpserver_test.py ++++ b/tornado/test/httpserver_test.py +@@ -1131,9 +1131,9 @@ class GzipUnsupportedTest(GzipBaseTest, AsyncHTTPTestCase): + # Gzip support is opt-in; without it the server fails to parse + # the body (but parsing form bodies is currently just a log message, + # not a fatal error). +- with ExpectLog(gen_log, "Unsupported Content-Encoding"): ++ with ExpectLog(gen_log, ".*Unsupported Content-Encoding"): + response = self.post_gzip("foo=bar") +- self.assertEqual(json_decode(response.body), {}) ++ self.assertEqual(response.code, 400) + + + class StreamingChunkSizeTest(AsyncHTTPTestCase): +diff --git a/tornado/test/httputil_test.py b/tornado/test/httputil_test.py +index 975900a..9494d0c 100644 +--- a/tornado/test/httputil_test.py ++++ b/tornado/test/httputil_test.py +@@ -12,7 +12,6 @@ from tornado.httputil import ( + ) + from tornado.escape import utf8, native_str + from tornado.log import gen_log +-from tornado.testing import ExpectLog + from tornado.test.util import ignore_deprecation + + import copy +@@ -195,7 +194,9 @@ Foo + b"\n", b"\r\n" + ) + args, files = form_data_args() +- with ExpectLog(gen_log, "multipart/form-data missing headers"): ++ with self.assertRaises( ++ HTTPInputError, msg="multipart/form-data missing headers" ++ ): + parse_multipart_form_data(b"1234", data, args, files) + self.assertEqual(files, {}) + +@@ -209,7 +210,7 @@ Foo + b"\n", b"\r\n" + ) + args, files = form_data_args() +- with ExpectLog(gen_log, "Invalid multipart/form-data"): ++ with self.assertRaises(HTTPInputError, msg="Invalid multipart/form-data"): + parse_multipart_form_data(b"1234", data, args, files) + self.assertEqual(files, {}) + +@@ -222,7 +223,7 @@ Foo--1234--""".replace( + b"\n", b"\r\n" + ) + args, files = form_data_args() +- with ExpectLog(gen_log, "Invalid multipart/form-data"): ++ with self.assertRaises(HTTPInputError, msg="Invalid multipart/form-data"): + parse_multipart_form_data(b"1234", data, args, files) + self.assertEqual(files, {}) + +@@ -236,7 +237,9 @@ Foo + b"\n", b"\r\n" + ) + args, files = form_data_args() +- with ExpectLog(gen_log, "multipart/form-data value missing name"): ++ with self.assertRaises( ++ HTTPInputError, msg="multipart/form-data value missing name" ++ ): + parse_multipart_form_data(b"1234", data, args, files) + self.assertEqual(files, {}) + +diff --git a/tornado/web.py b/tornado/web.py +index 0393964..8ec5601 100644 +--- a/tornado/web.py ++++ b/tornado/web.py +@@ -1751,6 +1751,14 @@ class RequestHandler(object): + try: + if self.request.method not in self.SUPPORTED_METHODS: + raise HTTPError(405) ++ ++ # If we're not in stream_request_body mode, this is the place where we parse the body. ++ if not _has_stream_request_body(self.__class__): ++ try: ++ self.request._parse_body() ++ except httputil.HTTPInputError as e: ++ raise HTTPError(400, "Invalid body: %s" % e) from e ++ + self.path_args = [self.decode_argument(arg) for arg in args] + self.path_kwargs = dict( + (k, self.decode_argument(v, name=k)) for (k, v) in kwargs.items() +@@ -1941,7 +1949,7 @@ def _has_stream_request_body(cls: Type[RequestHandler]) -> bool: + + + def removeslash( +- method: Callable[..., Optional[Awaitable[None]]] ++ method: Callable[..., Optional[Awaitable[None]]], + ) -> Callable[..., Optional[Awaitable[None]]]: + """Use this decorator to remove trailing slashes from the request path. + +@@ -1970,7 +1978,7 @@ def removeslash( + + + def addslash( +- method: Callable[..., Optional[Awaitable[None]]] ++ method: Callable[..., Optional[Awaitable[None]]], + ) -> Callable[..., Optional[Awaitable[None]]]: + """Use this decorator to add a missing trailing slash to the request path. + +@@ -2394,8 +2402,9 @@ class _HandlerDelegate(httputil.HTTPMessageDelegate): + if self.stream_request_body: + future_set_result_unless_cancelled(self.request._body_future, None) + else: ++ # Note that the body gets parsed in RequestHandler._execute so it can be in ++ # the right exception handler scope. + self.request.body = b"".join(self.chunks) +- self.request._parse_body() + self.execute() + + def on_connection_close(self) -> None: +@@ -3267,7 +3276,7 @@ class GZipContentEncoding(OutputTransform): + + + def authenticated( +- method: Callable[..., Optional[Awaitable[None]]] ++ method: Callable[..., Optional[Awaitable[None]]], + ) -> Callable[..., Optional[Awaitable[None]]]: + """Decorate methods with this to require that the user be logged in. + diff -Nru python-tornado-6.4.2/debian/patches/increase-timeout-rv64.patch python-tornado-6.4.2/debian/patches/increase-timeout-rv64.patch --- python-tornado-6.4.2/debian/patches/increase-timeout-rv64.patch 1970-01-01 07:30:00.000000000 +0730 +++ python-tornado-6.4.2/debian/patches/increase-timeout-rv64.patch 2025-05-22 21:16:28.000000000 +0800 @@ -0,0 +1,29 @@ +Description: increase timeout on riscv64 +Author: Bo YU <vi...@debian.org> +Bug: https://bugs.debian.org/1106130 +Forwarded: not-needed +Last-Update: 2025-05-19 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/tornado/test/autoreload_test.py ++++ b/tornado/test/autoreload_test.py +@@ -7,6 +7,7 @@ + import textwrap + import time + import unittest ++import platform + + + class AutoreloadTest(unittest.TestCase): +@@ -91,7 +92,10 @@ + for i in range(40): + if p.poll() is not None: + break +- time.sleep(0.1) ++ if platform.machine() == "riscv64": ++ time.sleep(1) ++ else: ++ time.sleep(0.1) + else: + p.kill() + raise Exception("subprocess failed to terminate") diff -Nru python-tornado-6.4.2/debian/patches/series python-tornado-6.4.2/debian/patches/series --- python-tornado-6.4.2/debian/patches/series 2024-11-29 21:09:50.000000000 +0800 +++ python-tornado-6.4.2/debian/patches/series 2025-05-22 21:16:28.000000000 +0800 @@ -4,3 +4,5 @@ 0007-Higher-test_gc-timeout.patch pythonpath-autoreload-test.patch disable-should-be-failing-test.patch +CVE-2025-47287.patch +increase-timeout-rv64.patch
diff -Nru python-tornado-6.4.2/debian/changelog python-tornado-6.4.2/debian/changelog --- python-tornado-6.4.2/debian/changelog 2025-05-18 23:43:40.000000000 +0800 +++ python-tornado-6.4.2/debian/changelog 2025-05-22 21:16:28.000000000 +0800 @@ -1,3 +1,10 @@ +python-tornado (6.4.2-3) unstable; urgency=medium + + * Team upload. + * Increase timeout on riscv64 to fix FTBFS issue. (Closes: #1106130) + + -- Bo YU <vi...@debian.org> Thu, 22 May 2025 21:16:28 +0800 + python-tornado (6.4.2-2) unstable; urgency=medium * Team upload. diff -Nru python-tornado-6.4.2/debian/.gitignore python-tornado-6.4.2/debian/.gitignore --- python-tornado-6.4.2/debian/.gitignore 2025-05-18 23:43:40.000000000 +0800 +++ python-tornado-6.4.2/debian/.gitignore 1970-01-01 07:30:00.000000000 +0730 @@ -1 +0,0 @@ -/files diff -Nru python-tornado-6.4.2/debian/patches/increase-timeout-rv64.patch python-tornado-6.4.2/debian/patches/increase-timeout-rv64.patch --- python-tornado-6.4.2/debian/patches/increase-timeout-rv64.patch 1970-01-01 07:30:00.000000000 +0730 +++ python-tornado-6.4.2/debian/patches/increase-timeout-rv64.patch 2025-05-22 21:16:28.000000000 +0800 @@ -0,0 +1,29 @@ +Description: increase timeout on riscv64 +Author: Bo YU <vi...@debian.org> +Bug: https://bugs.debian.org/1106130 +Forwarded: not-needed +Last-Update: 2025-05-19 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/tornado/test/autoreload_test.py ++++ b/tornado/test/autoreload_test.py +@@ -7,6 +7,7 @@ + import textwrap + import time + import unittest ++import platform + + + class AutoreloadTest(unittest.TestCase): +@@ -91,7 +92,10 @@ + for i in range(40): + if p.poll() is not None: + break +- time.sleep(0.1) ++ if platform.machine() == "riscv64": ++ time.sleep(1) ++ else: ++ time.sleep(0.1) + else: + p.kill() + raise Exception("subprocess failed to terminate") diff -Nru python-tornado-6.4.2/debian/patches/series python-tornado-6.4.2/debian/patches/series --- python-tornado-6.4.2/debian/patches/series 2025-05-18 23:43:40.000000000 +0800 +++ python-tornado-6.4.2/debian/patches/series 2025-05-22 21:16:28.000000000 +0800 @@ -5,3 +5,4 @@ pythonpath-autoreload-test.patch disable-should-be-failing-test.patch CVE-2025-47287.patch +increase-timeout-rv64.patch
signature.asc
Description: PGP signature