python3-tornado: patch CVE-2026-82397

Details:
https://nvd.nist.gov/vuln/detail/cve-2026-82397

Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
Ankur Tyagi authored and Anuj Mittal committed 2026-10-06 09:50:58 +05:30
1 parent 53a277b804
commit 7aabebd1f4
2 files changed
+150

No files matched your search

@@ -0,0 +1,149 @@
From eba9e5652af3edbf9d8f2375194b5c63362d0c91 Mon Sep 17 00:00:00 2001
From: Ben Darnell <ben@bendarnell.com>
Date: Wed, 5 Aug 2026 21:20:58 -0400
Subject: [PATCH] httputil: Enforce a new limit on the number of arguments in a
request
Large POST bodies can be very expensive to parse in the worst case,
so use the (new in Python 3.8) max_num_fields argument to limit the
cost. A new field in ParseBodyConfig allows users to configure this
limit. The default is 1000, which is the same as that used in php and
node.js.
(cherry picked from commit 8d6363ed7b69d5f0da806efe34d256627a2191de)
CVE: CVE-2026-82397
Upstream-Status: Backport [https://github.com/tornadoweb/tornado/commit/8d6363ed7b69d5f0da806efe34d256627a2191de]
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
tornado/escape.py | 16 ++++++++++++++--
tornado/httputil.py | 26 +++++++++++++++++++++++++-
tornado/test/httputil_test.py | 18 ++++++++++++++++++
3 files changed, 57 insertions(+), 3 deletions(-)
diff --git a/tornado/escape.py b/tornado/escape.py
index 8515bf58..a1c16b36 100644
--- a/tornado/escape.py
+++ b/tornado/escape.py
@@ -171,7 +171,11 @@ def url_unescape(
def parse_qs_bytes(
- qs: Union[str, bytes], keep_blank_values: bool = False, strict_parsing: bool = False
+ qs: Union[str, bytes],
+ keep_blank_values: bool = False,
+ strict_parsing: bool = False,
+ *,
+ max_num_fields: Optional[int] = None,
) -> Dict[str, List[bytes]]:
"""Parses a query string like urlparse.parse_qs,
but takes bytes and returns the values as byte strings.
@@ -179,13 +183,21 @@ def parse_qs_bytes(
Keys still become type str (interpreted as latin1 in python3!)
because it's too painful to keep them as byte strings in
python3 and in practice they're nearly always ascii anyway.
+
+ .. versionadded:: 6.5.8
+ The ``max_num_fields`` argument. ValueError is raised if this limit is exceeded.
"""
# This is gross, but python3 doesn't give us another way.
# Latin1 is the universal donor of character encodings.
if isinstance(qs, bytes):
qs = qs.decode("latin1")
result = urllib.parse.parse_qs(
- qs, keep_blank_values, strict_parsing, encoding="latin1", errors="strict"
+ qs,
+ keep_blank_values,
+ strict_parsing,
+ encoding="latin1",
+ errors="strict",
+ max_num_fields=max_num_fields,
)
encoded = {}
for k, v in result.items():
diff --git a/tornado/httputil.py b/tornado/httputil.py
index f698db21..b80c796c 100644
--- a/tornado/httputil.py
+++ b/tornado/httputil.py
@@ -947,6 +947,23 @@ class ParseMultipartConfig:
"""
+@dataclasses.dataclass
+class ParseUrlEncodedConfig:
+ """This class configures the parsing of ``application/x-www-form-urlencoded`` request bodies.
+
+ Its primary purpose is to place limits on the size and complexity of request messages
+ to avoid potential denial-of-service attacks.
+
+ .. versionadded:: 6.5.8
+ """
+
+ max_arguments: int = 1000
+ """The maximum number of arguments accepted in a urlencoded request.
+
+ Each ``<input>`` element in an HTML form corresponds to at least one argument.
+ """
+
+
@dataclasses.dataclass
class ParseBodyConfig:
"""This class configures the parsing of request bodies.
@@ -957,6 +974,9 @@ class ParseBodyConfig:
multipart: ParseMultipartConfig = dataclasses.field(
default_factory=ParseMultipartConfig
)
+ urlencoded: ParseUrlEncodedConfig = dataclasses.field(
+ default_factory=ParseUrlEncodedConfig
+ )
"""Configuration for ``multipart/form-data`` request bodies."""
@@ -1015,7 +1035,11 @@ def parse_body_arguments(
)
try:
# real charset decoding will happen in RequestHandler.decode_argument()
- uri_arguments = parse_qs_bytes(body, keep_blank_values=True)
+ uri_arguments = parse_qs_bytes(
+ body,
+ keep_blank_values=True,
+ max_num_fields=config.urlencoded.max_arguments,
+ )
except Exception as e:
raise HTTPInputError("Invalid x-www-form-urlencoded body: %s" % e) from e
for name, values in uri_arguments.items():
diff --git a/tornado/test/httputil_test.py b/tornado/test/httputil_test.py
index 92683ae9..afb15879 100644
--- a/tornado/test/httputil_test.py
+++ b/tornado/test/httputil_test.py
@@ -1,4 +1,5 @@
from tornado.httputil import (
+ parse_body_arguments,
url_concat,
parse_multipart_form_data,
HTTPHeaders,
@@ -95,6 +96,23 @@ class QsParseTest(unittest.TestCase):
self.assertIn(("b", "2"), qsl)
+class UrlEncodedDataTest(unittest.TestCase):
+ def test_urlencoded_data(self):
+ data = b"a=1&b=2&a=3"
+ args, files = form_data_args()
+ parse_body_arguments("application/x-www-form-urlencoded", data, args, files)
+ self.assertEqual(args["a"], [b"1", b"3"])
+ self.assertEqual(args["b"], [b"2"])
+ self.assertEqual(files, {})
+
+ def test_max_arguments(self):
+ data = b"".join(b"a=1&" for _ in range(1001))
+ args, files = form_data_args()
+ with self.assertRaises(HTTPInputError) as cm:
+ parse_body_arguments("application/x-www-form-urlencoded", data, args, files)
+ self.assertIn("Max number of fields exceeded", str(cm.exception))
+
+
class MultipartFormDataTest(unittest.TestCase):
def test_file_upload(self):
data = b"""\
@@ -9,6 +9,7 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57"
SRC_URI[sha256sum] = "66c513a76cda70d53907bc27cf1447557699c2e95aa48ba27a442ff61c3ddfc2"
SRC_URI += "file://CVE-2026-91990.patch \
file://CVE-2026-82397.patch \
"
inherit pypi python_setuptools_build_meta