mirror of
https://git.freebsd.org/ports.git
synced 2026-06-02 11:08:52 +00:00
lang/python314: Update to 3.14.5
This release swaps out the incremental garbage collector for the generational one used in 3.13 due to reports of memory pressure. Backport the post-release upstream fix for: Heap Buffer Overflow in pyexpat Character Data Buffering #148441 which is believed to be only triggered in rare circumstances. Changelog: https://www.python.org/downloads/release/python-3145/ PR: 295200 MFH: 2026Q2
This commit is contained in:
committed by
Daniel Engberg
parent
b8c7e0b115
commit
ecdfdb02e3
@@ -1,6 +1,6 @@
|
||||
PORTNAME= python
|
||||
DISTVERSION= ${PYTHON_DISTVERSION} # see Makefile.version
|
||||
PORTREVISION= 3
|
||||
PORTREVISION= 0
|
||||
CATEGORIES= lang python
|
||||
MASTER_SITES= PYTHON/ftp/python/${DISTVERSION:C/[a-z].*//}
|
||||
PKGNAMESUFFIX= ${PYTHON_SUFFIX}
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
# 1. Update python documentation (lang/python-doc-*)
|
||||
# Run "make -C lang/python-doc-html makesum"
|
||||
# 2. Remove PORTREVISION in Makefile
|
||||
PYTHON_DISTVERSION= 3.14.4
|
||||
PYTHON_DISTVERSION= 3.14.5
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
TIMESTAMP = 1775640582
|
||||
SHA256 (python/Python-3.14.4.tar.xz) = d923c51303e38e249136fc1bdf3568d56ecb03214efdef48516176d3d7faaef8
|
||||
SIZE (python/Python-3.14.4.tar.xz) = 23855332
|
||||
TIMESTAMP = 1778513616
|
||||
SHA256 (python/Python-3.14.5.tar.xz) = 7e32597b99e5d9a39abed35de4693fa169df3e5850d4c334337ffd6a19a36db6
|
||||
SIZE (python/Python-3.14.5.tar.xz) = 23903332
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
From afdd351544e8112d4070a31f2218f99256697472 Mon Sep 17 00:00:00 2001
|
||||
From: Seth Larson <seth@python.org>
|
||||
Date: Fri, 10 Apr 2026 10:21:42 -0500
|
||||
Subject: [PATCH] gh-146211: Reject CR/LF in HTTP tunnel request headers
|
||||
(GH-146212) (cherry picked from commit
|
||||
05ed7ce7ae9e17c23a04085b2539fe6d6d3cef69)
|
||||
|
||||
Co-authored-by: Seth Larson <seth@python.org>
|
||||
Co-authored-by: Illia Volochii <illia.volochii@gmail.com>
|
||||
---
|
||||
Lib/http/client.py | 11 ++++-
|
||||
Lib/test/test_httplib.py | 45 +++++++++++++++++++
|
||||
...-03-20-09-29-42.gh-issue-146211.PQVbs7.rst | 2 +
|
||||
3 files changed, 57 insertions(+), 1 deletion(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Security/2026-03-20-09-29-42.gh-issue-146211.PQVbs7.rst
|
||||
|
||||
diff --git a/Lib/http/client.py b/Lib/http/client.py
|
||||
index 77f8d26291dfc2..6fb7d254ea9c27 100644
|
||||
--- ./Lib/http/client.py
|
||||
+++ b/Lib/http/client.py
|
||||
@@ -972,13 +972,22 @@ def _wrap_ipv6(self, ip):
|
||||
return ip
|
||||
|
||||
def _tunnel(self):
|
||||
+ if _contains_disallowed_url_pchar_re.search(self._tunnel_host):
|
||||
+ raise ValueError('Tunnel host can\'t contain control characters %r'
|
||||
+ % (self._tunnel_host,))
|
||||
connect = b"CONNECT %s:%d %s\r\n" % (
|
||||
self._wrap_ipv6(self._tunnel_host.encode("idna")),
|
||||
self._tunnel_port,
|
||||
self._http_vsn_str.encode("ascii"))
|
||||
headers = [connect]
|
||||
for header, value in self._tunnel_headers.items():
|
||||
- headers.append(f"{header}: {value}\r\n".encode("latin-1"))
|
||||
+ header_bytes = header.encode("latin-1")
|
||||
+ value_bytes = value.encode("latin-1")
|
||||
+ if not _is_legal_header_name(header_bytes):
|
||||
+ raise ValueError('Invalid header name %r' % (header_bytes,))
|
||||
+ if _is_illegal_header_value(value_bytes):
|
||||
+ raise ValueError('Invalid header value %r' % (value_bytes,))
|
||||
+ headers.append(b"%s: %s\r\n" % (header_bytes, value_bytes))
|
||||
headers.append(b"\r\n")
|
||||
# Making a single send() call instead of one per line encourages
|
||||
# the host OS to use a more optimal packet size instead of
|
||||
diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py
|
||||
index bcb828edec7c39..6f3eac6b98a4de 100644
|
||||
--- ./Lib/test/test_httplib.py
|
||||
+++ b/Lib/test/test_httplib.py
|
||||
@@ -369,6 +369,51 @@ def test_invalid_headers(self):
|
||||
with self.assertRaisesRegex(ValueError, 'Invalid header'):
|
||||
conn.putheader(name, value)
|
||||
|
||||
+ def test_invalid_tunnel_headers(self):
|
||||
+ cases = (
|
||||
+ ('Invalid\r\nName', 'ValidValue'),
|
||||
+ ('Invalid\rName', 'ValidValue'),
|
||||
+ ('Invalid\nName', 'ValidValue'),
|
||||
+ ('\r\nInvalidName', 'ValidValue'),
|
||||
+ ('\rInvalidName', 'ValidValue'),
|
||||
+ ('\nInvalidName', 'ValidValue'),
|
||||
+ (' InvalidName', 'ValidValue'),
|
||||
+ ('\tInvalidName', 'ValidValue'),
|
||||
+ ('Invalid:Name', 'ValidValue'),
|
||||
+ (':InvalidName', 'ValidValue'),
|
||||
+ ('ValidName', 'Invalid\r\nValue'),
|
||||
+ ('ValidName', 'Invalid\rValue'),
|
||||
+ ('ValidName', 'Invalid\nValue'),
|
||||
+ ('ValidName', 'InvalidValue\r\n'),
|
||||
+ ('ValidName', 'InvalidValue\r'),
|
||||
+ ('ValidName', 'InvalidValue\n'),
|
||||
+ )
|
||||
+ for name, value in cases:
|
||||
+ with self.subTest((name, value)):
|
||||
+ conn = client.HTTPConnection('example.com')
|
||||
+ conn.set_tunnel('tunnel', headers={
|
||||
+ name: value
|
||||
+ })
|
||||
+ conn.sock = FakeSocket('')
|
||||
+ with self.assertRaisesRegex(ValueError, 'Invalid header'):
|
||||
+ conn._tunnel() # Called in .connect()
|
||||
+
|
||||
+ def test_invalid_tunnel_host(self):
|
||||
+ cases = (
|
||||
+ 'invalid\r.host',
|
||||
+ '\ninvalid.host',
|
||||
+ 'invalid.host\r\n',
|
||||
+ 'invalid.host\x00',
|
||||
+ 'invalid host',
|
||||
+ )
|
||||
+ for tunnel_host in cases:
|
||||
+ with self.subTest(tunnel_host):
|
||||
+ conn = client.HTTPConnection('example.com')
|
||||
+ conn.set_tunnel(tunnel_host)
|
||||
+ conn.sock = FakeSocket('')
|
||||
+ with self.assertRaisesRegex(ValueError, 'Tunnel host can\'t contain control characters'):
|
||||
+ conn._tunnel() # Called in .connect()
|
||||
+
|
||||
def test_headers_debuglevel(self):
|
||||
body = (
|
||||
b'HTTP/1.1 200 OK\r\n'
|
||||
diff --git a/Misc/NEWS.d/next/Security/2026-03-20-09-29-42.gh-issue-146211.PQVbs7.rst b/Misc/NEWS.d/next/Security/2026-03-20-09-29-42.gh-issue-146211.PQVbs7.rst
|
||||
new file mode 100644
|
||||
index 00000000000000..4993633b8ebebb
|
||||
--- /dev/null
|
||||
+++ ./Misc/NEWS.d/next/Security/2026-03-20-09-29-42.gh-issue-146211.PQVbs7.rst
|
||||
@@ -0,0 +1,2 @@
|
||||
+Reject CR/LF characters in tunnel request headers for the
|
||||
+HTTPConnection.set_tunnel() method.
|
||||
@@ -1,83 +0,0 @@
|
||||
From ab8704a8e05e2f926c10f994e4085e8726048fa4 Mon Sep 17 00:00:00 2001
|
||||
From: Joshua Swanson <22283299+joshuaswanson@users.noreply.github.com>
|
||||
Date: Tue, 7 Apr 2026 16:10:34 +0200
|
||||
Subject: [PATCH] gh-146333: Fix quadratic regex backtracking in configparser
|
||||
option parsing (GH-146399)
|
||||
|
||||
Use negative lookahead in option regex to prevent backtracking, and to avoid changing logic outside the regexes (since people could use the regex directly).
|
||||
(cherry picked from commit 7e0a0be4097f9d29d66fe23f5af86f18a34ed7dd)
|
||||
|
||||
Co-authored-by: Joshua Swanson <22283299+joshuaswanson@users.noreply.github.com>
|
||||
---
|
||||
Lib/configparser.py | 8 ++++++--
|
||||
Lib/test/test_configparser.py | 20 +++++++++++++++++++
|
||||
...3-25-00-51-03.gh-issue-146333.LqdL__bn.rst | 3 +++
|
||||
3 files changed, 29 insertions(+), 2 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Security/2026-03-25-00-51-03.gh-issue-146333.LqdL__bn.rst
|
||||
|
||||
diff --git a/Lib/configparser.py b/Lib/configparser.py
|
||||
index d435a5c2fe0da2..e76647d339e913 100644
|
||||
--- ./Lib/configparser.py
|
||||
+++ b/Lib/configparser.py
|
||||
@@ -613,7 +613,9 @@ class RawConfigParser(MutableMapping):
|
||||
\] # ]
|
||||
"""
|
||||
_OPT_TMPL = r"""
|
||||
- (?P<option>.*?) # very permissive!
|
||||
+ (?P<option> # very permissive!
|
||||
+ (?:(?!{delim})\S)* # non-delimiter non-whitespace
|
||||
+ (?:\s+(?:(?!{delim})\S)+)*) # optionally more words
|
||||
\s*(?P<vi>{delim})\s* # any number of space/tab,
|
||||
# followed by any of the
|
||||
# allowed delimiters,
|
||||
@@ -621,7 +623,9 @@ class RawConfigParser(MutableMapping):
|
||||
(?P<value>.*)$ # everything up to eol
|
||||
"""
|
||||
_OPT_NV_TMPL = r"""
|
||||
- (?P<option>.*?) # very permissive!
|
||||
+ (?P<option> # very permissive!
|
||||
+ (?:(?!{delim})\S)* # non-delimiter non-whitespace
|
||||
+ (?:\s+(?:(?!{delim})\S)+)*) # optionally more words
|
||||
\s*(?: # any number of space/tab,
|
||||
(?P<vi>{delim})\s* # optionally followed by
|
||||
# any of the allowed
|
||||
diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py
|
||||
index 1bfb53ccbb1398..d7c4f19c1a5ef0 100644
|
||||
--- ./Lib/test/test_configparser.py
|
||||
+++ b/Lib/test/test_configparser.py
|
||||
@@ -2270,6 +2270,26 @@ def test_section_bracket_in_key(self):
|
||||
output.close()
|
||||
|
||||
|
||||
+class ReDoSTestCase(unittest.TestCase):
|
||||
+ """Regression tests for quadratic regex backtracking (gh-146333)."""
|
||||
+
|
||||
+ def test_option_regex_does_not_backtrack(self):
|
||||
+ # A line with many spaces between non-delimiter characters
|
||||
+ # should be parsed in linear time, not quadratic.
|
||||
+ parser = configparser.RawConfigParser()
|
||||
+ content = "[section]\n" + "x" + " " * 40000 + "y" + "\n"
|
||||
+ # This should complete almost instantly. Before the fix,
|
||||
+ # it would take over a minute due to catastrophic backtracking.
|
||||
+ with self.assertRaises(configparser.ParsingError):
|
||||
+ parser.read_string(content)
|
||||
+
|
||||
+ def test_option_regex_no_value_does_not_backtrack(self):
|
||||
+ parser = configparser.RawConfigParser(allow_no_value=True)
|
||||
+ content = "[section]\n" + "x" + " " * 40000 + "y" + "\n"
|
||||
+ parser.read_string(content)
|
||||
+ self.assertTrue(parser.has_option("section", "x" + " " * 40000 + "y"))
|
||||
+
|
||||
+
|
||||
class MiscTestCase(unittest.TestCase):
|
||||
def test__all__(self):
|
||||
support.check__all__(self, configparser, not_exported={"Error"})
|
||||
diff --git a/Misc/NEWS.d/next/Security/2026-03-25-00-51-03.gh-issue-146333.LqdL__bn.rst b/Misc/NEWS.d/next/Security/2026-03-25-00-51-03.gh-issue-146333.LqdL__bn.rst
|
||||
new file mode 100644
|
||||
index 00000000000000..96d86ecc0a0fb3
|
||||
--- /dev/null
|
||||
+++ ./Misc/NEWS.d/next/Security/2026-03-25-00-51-03.gh-issue-146333.LqdL__bn.rst
|
||||
@@ -0,0 +1,3 @@
|
||||
+Fix quadratic backtracking in :class:`configparser.RawConfigParser` option
|
||||
+parsing regexes (``OPTCRE`` and ``OPTCRE_NV``). A crafted configuration line
|
||||
+with many whitespace characters could cause excessive CPU usage.
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
From f529b9470752c28ab69c96f31b0dbc10db69b404 Mon Sep 17 00:00:00 2001
|
||||
From: Stan Ulbrych <stan@python.org>
|
||||
Date: Mon, 13 Apr 2026 20:02:52 +0100
|
||||
Subject: [PATCH] gh-148169: Fix webbrowser `%action` substitution bypass of
|
||||
dash-prefix check (GH-148170) (cherry picked from commit
|
||||
d22922c8a7958353689dc4763dd72da2dea03fff)
|
||||
|
||||
Co-authored-by: Stan Ulbrych <stan@python.org>
|
||||
---
|
||||
Lib/test/test_webbrowser.py | 9 +++++++++
|
||||
Lib/webbrowser.py | 5 +++--
|
||||
.../2026-03-31-09-15-51.gh-issue-148169.EZJzz2.rst | 2 ++
|
||||
3 files changed, 14 insertions(+), 2 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Security/2026-03-31-09-15-51.gh-issue-148169.EZJzz2.rst
|
||||
|
||||
diff --git a/Lib/test/test_webbrowser.py b/Lib/test/test_webbrowser.py
|
||||
index 404b3a31a5d2c9..bfbcf112b0b085 100644
|
||||
--- ./Lib/test/test_webbrowser.py
|
||||
+++ b/Lib/test/test_webbrowser.py
|
||||
@@ -119,6 +119,15 @@ def test_open_bad_new_parameter(self):
|
||||
arguments=[URL],
|
||||
kw=dict(new=999))
|
||||
|
||||
+ def test_reject_action_dash_prefixes(self):
|
||||
+ browser = self.browser_class(name=CMD_NAME)
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ browser.open('%action--incognito')
|
||||
+ # new=1: action is "--new-window", so "%action" itself expands to
|
||||
+ # a dash-prefixed flag even with no dash in the original URL.
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ browser.open('%action', new=1)
|
||||
+
|
||||
|
||||
class EdgeCommandTest(CommandTestMixin, unittest.TestCase):
|
||||
|
||||
diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py
|
||||
index 0e0b5034e5f53d..97aad6eea509eb 100644
|
||||
--- ./Lib/webbrowser.py
|
||||
+++ b/Lib/webbrowser.py
|
||||
@@ -274,7 +274,6 @@ def _invoke(self, args, remote, autoraise, url=None):
|
||||
|
||||
def open(self, url, new=0, autoraise=True):
|
||||
sys.audit("webbrowser.open", url)
|
||||
- self._check_url(url)
|
||||
if new == 0:
|
||||
action = self.remote_action
|
||||
elif new == 1:
|
||||
@@ -288,7 +287,9 @@ def open(self, url, new=0, autoraise=True):
|
||||
raise Error("Bad 'new' parameter to open(); "
|
||||
f"expected 0, 1, or 2, got {new}")
|
||||
|
||||
- args = [arg.replace("%s", url).replace("%action", action)
|
||||
+ self._check_url(url.replace("%action", action))
|
||||
+
|
||||
+ args = [arg.replace("%action", action).replace("%s", url)
|
||||
for arg in self.remote_args]
|
||||
args = [arg for arg in args if arg]
|
||||
success = self._invoke(args, True, autoraise, url)
|
||||
diff --git a/Misc/NEWS.d/next/Security/2026-03-31-09-15-51.gh-issue-148169.EZJzz2.rst b/Misc/NEWS.d/next/Security/2026-03-31-09-15-51.gh-issue-148169.EZJzz2.rst
|
||||
new file mode 100644
|
||||
index 00000000000000..45cdeebe1b6d64
|
||||
--- /dev/null
|
||||
+++ ./Misc/NEWS.d/next/Security/2026-03-31-09-15-51.gh-issue-148169.EZJzz2.rst
|
||||
@@ -0,0 +1,2 @@
|
||||
+A bypass in :mod:`webbrowser` allowed URLs prefixed with ``%action`` to pass
|
||||
+the dash-prefix safety check.
|
||||
@@ -1,65 +0,0 @@
|
||||
From c8d8173c4b06d06902c99ec010ad785a30952880 Mon Sep 17 00:00:00 2001
|
||||
From: Stan Ulbrych <stan@python.org>
|
||||
Date: Mon, 13 Apr 2026 02:14:54 +0100
|
||||
Subject: [PATCH] gh-148395: Fix a possible UAF in
|
||||
`{LZMA,BZ2,_Zlib}Decompressor` (GH-148396)
|
||||
|
||||
Fix dangling input pointer after `MemoryError` in _lzma/_bz2/_ZlibDecompressor.decompress
|
||||
(cherry picked from commit 8fc66aef6d7b3ae58f43f5c66f9366cc8cbbfcd2)
|
||||
|
||||
Co-authored-by: Stan Ulbrych <stan@python.org>
|
||||
---
|
||||
.../Security/2026-04-10-16-28-21.gh-issue-148395.kfzm0G.rst | 5 +++++
|
||||
Modules/_bz2module.c | 1 +
|
||||
Modules/_lzmamodule.c | 1 +
|
||||
Modules/zlibmodule.c | 1 +
|
||||
4 files changed, 8 insertions(+)
|
||||
create mode 100644 Misc/NEWS.d/next/Security/2026-04-10-16-28-21.gh-issue-148395.kfzm0G.rst
|
||||
|
||||
diff --git a/Misc/NEWS.d/next/Security/2026-04-10-16-28-21.gh-issue-148395.kfzm0G.rst b/Misc/NEWS.d/next/Security/2026-04-10-16-28-21.gh-issue-148395.kfzm0G.rst
|
||||
new file mode 100644
|
||||
index 00000000000000..9502189ab199c1
|
||||
--- /dev/null
|
||||
+++ ./Misc/NEWS.d/next/Security/2026-04-10-16-28-21.gh-issue-148395.kfzm0G.rst
|
||||
@@ -0,0 +1,5 @@
|
||||
+Fix a dangling input pointer in :class:`lzma.LZMADecompressor`,
|
||||
+:class:`bz2.BZ2Decompressor`, and internal :class:`!zlib._ZlibDecompressor`
|
||||
+when memory allocation fails with :exc:`MemoryError`, which could let a
|
||||
+subsequent :meth:`!decompress` call read or write through a stale pointer to
|
||||
+the already-released caller buffer.
|
||||
diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c
|
||||
index 9e85e0de42cd8d..055ce82e7d2863 100644
|
||||
--- ./Modules/_bz2module.c
|
||||
+++ b/Modules/_bz2module.c
|
||||
@@ -593,6 +593,7 @@ decompress(BZ2Decompressor *d, char *data, size_t len, Py_ssize_t max_length)
|
||||
return result;
|
||||
|
||||
error:
|
||||
+ bzs->next_in = NULL;
|
||||
Py_XDECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
diff --git a/Modules/_lzmamodule.c b/Modules/_lzmamodule.c
|
||||
index 462c2181fa6036..6785dc56730c5c 100644
|
||||
--- ./Modules/_lzmamodule.c
|
||||
+++ b/Modules/_lzmamodule.c
|
||||
@@ -1120,6 +1120,7 @@ decompress(Decompressor *d, uint8_t *data, size_t len, Py_ssize_t max_length)
|
||||
return result;
|
||||
|
||||
error:
|
||||
+ lzs->next_in = NULL;
|
||||
Py_XDECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c
|
||||
index 5b6b0c5cac864a..a86aa5fdbb576c 100644
|
||||
--- ./Modules/zlibmodule.c
|
||||
+++ b/Modules/zlibmodule.c
|
||||
@@ -1675,6 +1675,7 @@ decompress(ZlibDecompressor *self, uint8_t *data,
|
||||
return result;
|
||||
|
||||
error:
|
||||
+ self->zst.next_in = NULL;
|
||||
Py_XDECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
From 6588ca5b642a0f878197fc31afb6bfa424fd7219 Mon Sep 17 00:00:00 2001
|
||||
From: "Miss Islington (bot)"
|
||||
<31488909+miss-islington@users.noreply.github.com>
|
||||
Date: Sun, 10 May 2026 16:08:59 +0200
|
||||
Subject: [PATCH] [3.14] gh-148441: Avoid integer overflow in Expat's
|
||||
CharacterDataHandler (GH-148904) (#149638)
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
gh-148441: Avoid integer overflow in Expat's CharacterDataHandler (GH-148904)
|
||||
(cherry picked from commit bc1be4f6174086b4a46e3fe656552f5bb4e6e7b2)
|
||||
|
||||
Co-authored-by: ByteFlow <fakeshadow1337@gmail.com>
|
||||
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
|
||||
---
|
||||
Lib/test/test_pyexpat.py | 14 ++++++++++++++
|
||||
.../2026-04-23-12-50-15.gh-issue-148441.zvpCkR.rst | 4 ++++
|
||||
Modules/pyexpat.c | 2 +-
|
||||
3 files changed, 19 insertions(+), 1 deletion(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Library/2026-04-23-12-50-15.gh-issue-148441.zvpCkR.rst
|
||||
|
||||
diff --git ./Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py
|
||||
index 465f65a03b9e15..6eda89ad2cdf2a 100644
|
||||
--- ./Lib/test/test_pyexpat.py
|
||||
+++ b/Lib/test/test_pyexpat.py
|
||||
@@ -672,6 +672,20 @@ def test_change_size_2(self):
|
||||
parser.Parse(xml2, True)
|
||||
self.assertEqual(self.n, 4)
|
||||
|
||||
+ @support.requires_resource('cpu')
|
||||
+ @support.requires_resource('walltime')
|
||||
+ @support.bigmemtest(size=2**31, memuse=4, dry_run=False)
|
||||
+ def test_large_character_data_does_not_crash(self):
|
||||
+ # See https://github.com/python/cpython/issues/148441
|
||||
+ parser = expat.ParserCreate()
|
||||
+ parser.buffer_text = True
|
||||
+ parser.buffer_size = 2**31 - 1 # INT_MAX
|
||||
+ N = 2049 * (1 << 20) - 3 # Character data greater than INT_MAX
|
||||
+ self.assertGreater(N, parser.buffer_size)
|
||||
+ parser.CharacterDataHandler = lambda text: None
|
||||
+ xml_data = b"<r>" + b"A" * N + b"</r>"
|
||||
+ self.assertEqual(parser.Parse(xml_data, True), 1)
|
||||
+
|
||||
class ElementDeclHandlerTest(unittest.TestCase):
|
||||
def test_trigger_leak(self):
|
||||
# Unfixed, this test would leak the memory of the so-called
|
||||
diff --git a/Misc/NEWS.d/next/Library/2026-04-23-12-50-15.gh-issue-148441.zvpCkR.rst b/Misc/NEWS.d/next/Library/2026-04-23-12-50-15.gh-issue-148441.zvpCkR.rst
|
||||
new file mode 100644
|
||||
index 00000000000000..762815270e4d40
|
||||
--- /dev/null
|
||||
+++ ./Misc/NEWS.d/next/Library/2026-04-23-12-50-15.gh-issue-148441.zvpCkR.rst
|
||||
@@ -0,0 +1,4 @@
|
||||
+:mod:`xml.parsers.expat`: prevent a crash in
|
||||
+:meth:`~xml.parsers.expat.xmlparser.CharacterDataHandler`
|
||||
+when the character data size exceeds the parser's
|
||||
+:attr:`buffer size <xml.parsers.expat.xmlparser.buffer_size>`.
|
||||
diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c
|
||||
index f82f8456e489eb..c9dc5e2211ecd5 100644
|
||||
--- ./Modules/pyexpat.c
|
||||
+++ b/Modules/pyexpat.c
|
||||
@@ -389,7 +389,7 @@ my_CharacterDataHandler(void *userData, const XML_Char *data, int len)
|
||||
if (self->buffer == NULL)
|
||||
call_character_handler(self, data, len);
|
||||
else {
|
||||
- if ((self->buffer_used + len) > self->buffer_size) {
|
||||
+ if (len > (self->buffer_size - self->buffer_used)) {
|
||||
if (flush_character_buffer(self) < 0)
|
||||
return;
|
||||
/* handler might have changed; drop the rest on the floor
|
||||
@@ -1847,7 +1847,7 @@ lib/python%%XYDOT%%/ensurepip/__pycache__/__main__.cpython-%%XY%%.pyc
|
||||
lib/python%%XYDOT%%/ensurepip/__pycache__/_uninstall.cpython-%%XY%%.opt-1.pyc
|
||||
lib/python%%XYDOT%%/ensurepip/__pycache__/_uninstall.cpython-%%XY%%.opt-2.pyc
|
||||
lib/python%%XYDOT%%/ensurepip/__pycache__/_uninstall.cpython-%%XY%%.pyc
|
||||
lib/python%%XYDOT%%/ensurepip/_bundled/pip-26.0.1-py3-none-any.whl
|
||||
lib/python%%XYDOT%%/ensurepip/_bundled/pip-26.1.1-py3-none-any.whl
|
||||
lib/python%%XYDOT%%/ensurepip/_uninstall.py
|
||||
lib/python%%XYDOT%%/enum.py
|
||||
lib/python%%XYDOT%%/filecmp.py
|
||||
@@ -2908,9 +2908,6 @@ lib/python%%XYDOT%%/test/__pycache__/_test_eintr.cpython-%%XY%%.pyc
|
||||
lib/python%%XYDOT%%/test/__pycache__/_test_embed_structseq.cpython-%%XY%%.opt-1.pyc
|
||||
lib/python%%XYDOT%%/test/__pycache__/_test_embed_structseq.cpython-%%XY%%.opt-2.pyc
|
||||
lib/python%%XYDOT%%/test/__pycache__/_test_embed_structseq.cpython-%%XY%%.pyc
|
||||
lib/python%%XYDOT%%/test/__pycache__/_test_gc_fast_cycles.cpython-%%XY%%.opt-1.pyc
|
||||
lib/python%%XYDOT%%/test/__pycache__/_test_gc_fast_cycles.cpython-%%XY%%.opt-2.pyc
|
||||
lib/python%%XYDOT%%/test/__pycache__/_test_gc_fast_cycles.cpython-%%XY%%.pyc
|
||||
lib/python%%XYDOT%%/test/__pycache__/_test_monitoring_shutdown.cpython-%%XY%%.opt-1.pyc
|
||||
lib/python%%XYDOT%%/test/__pycache__/_test_monitoring_shutdown.cpython-%%XY%%.opt-2.pyc
|
||||
lib/python%%XYDOT%%/test/__pycache__/_test_monitoring_shutdown.cpython-%%XY%%.pyc
|
||||
@@ -4233,7 +4230,6 @@ lib/python%%XYDOT%%/test/_crossinterp_definitions.py
|
||||
lib/python%%XYDOT%%/test/_test_atexit.py
|
||||
lib/python%%XYDOT%%/test/_test_eintr.py
|
||||
lib/python%%XYDOT%%/test/_test_embed_structseq.py
|
||||
lib/python%%XYDOT%%/test/_test_gc_fast_cycles.py
|
||||
lib/python%%XYDOT%%/test/_test_monitoring_shutdown.py
|
||||
lib/python%%XYDOT%%/test/_test_multiprocessing.py
|
||||
lib/python%%XYDOT%%/test/_test_venv_multiprocessing.py
|
||||
@@ -6679,6 +6675,7 @@ lib/python%%XYDOT%%/test/test_json/__pycache__/test_tool.cpython-%%XY%%.pyc
|
||||
lib/python%%XYDOT%%/test/test_json/__pycache__/test_unicode.cpython-%%XY%%.opt-1.pyc
|
||||
lib/python%%XYDOT%%/test/test_json/__pycache__/test_unicode.cpython-%%XY%%.opt-2.pyc
|
||||
lib/python%%XYDOT%%/test/test_json/__pycache__/test_unicode.cpython-%%XY%%.pyc
|
||||
lib/python%%XYDOT%%/test/test_json/json_lines.jsonl
|
||||
lib/python%%XYDOT%%/test/test_json/test_decode.py
|
||||
lib/python%%XYDOT%%/test/test_json/test_default.py
|
||||
lib/python%%XYDOT%%/test/test_json/test_dump.py
|
||||
|
||||
Reference in New Issue
Block a user