https://github.com/python/cpython/commit/af2851ae54e337489d496c0090ac5d6fbb24fa2c
commit: af2851ae54e337489d496c0090ac5d6fbb24fa2c
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: gpshead <[email protected]>
date: 2026-11-02T11:37:09Z
summary:

[3.14] gh-160316: Make `re.Pattern.match()` return `None` when `pos > endpos` 
(GH-160317) (#161044)

Pattern.match(), fullmatch() and the scanner match() ran
the engine with the start position past the end position, which could
return an empty match or read past the end of the string.

ANT-2026-NMTUWIC8
(cherry picked from commit 6b58f9a6a12a3c514a755e5e86dc9b9f36bacb9d)

Co-authored-by: Gregory P. Smith <[email protected]>

* 3.14-ize the news entry

Co-authored-by: Hugo van Kemenade <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-10-22-08-17-51.gh-issue-160316.sRe9pQ.rst
M Lib/test/test_re.py
M Modules/_sre/sre.c

diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py
index 021a8a3fe29cd6f..13d48339c932ca7 100644
--- a/Lib/test/test_re.py
+++ b/Lib/test/test_re.py
@@ -2194,6 +2194,25 @@ def test_keyword_parameters(self):
             pat.scanner(string='abracadabra', pos=3, 
endpos=10).search().span(),
             (7, 9))
 
+    def test_pos_greater_than_endpos(self):
+        # If endpos is less than pos, no match will be found.
+        for pattern in ['', r'$', r'^$', r'\b$', r'(?:x|$)']:
+            for string in ['abc\n', b'abc\n', bytearray(b'abc\n')]:
+                with self.subTest(pattern=pattern, string=string):
+                    if isinstance(string, str):
+                        p = re.compile(pattern, re.MULTILINE)
+                    else:
+                        p = re.compile(pattern.encode(), re.MULTILINE)
+                    for pos, endpos in [(3, 0), (4, 0), (4, 3), (2, 1)]:
+                        self.assertIsNone(p.match(string, pos, endpos))
+                        self.assertIsNone(p.fullmatch(string, pos, endpos))
+                        self.assertIsNone(p.search(string, pos, endpos))
+                        self.assertIsNone(
+                            p.scanner(string, pos, endpos).match())
+                        self.assertIsNone(
+                            p.scanner(string, pos, endpos).search())
+                        self.assertEqual(p.findall(string, pos, endpos), [])
+
     def test_bug_20998(self):
         # Issue #20998: Fullmatch of repeated single character pattern
         # with ignore case.
diff --git 
a/Misc/NEWS.d/next/Library/2026-10-22-08-17-51.gh-issue-160316.sRe9pQ.rst 
b/Misc/NEWS.d/next/Library/2026-10-22-08-17-51.gh-issue-160316.sRe9pQ.rst
new file mode 100644
index 000000000000000..c7f7865dc715b4b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-22-08-17-51.gh-issue-160316.sRe9pQ.rst
@@ -0,0 +1,5 @@
+Fix :meth:`re.Pattern.match`,
+:meth:`re.Pattern.fullmatch` and the ``match()`` method
+of scanner objects returning an empty match, or reading past the end of the
+string, when *pos* is greater than *endpos*.  They now return ``None``, as
+documented.
diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c
index ace6b17ee2d93cf..2e1bbe7b0746c26 100644
--- a/Modules/_sre/sre.c
+++ b/Modules/_sre/sre.c
@@ -746,6 +746,10 @@ pattern_dealloc(PyObject *self)
 LOCAL(Py_ssize_t)
 sre_match(SRE_STATE* state, SRE_CODE* pattern)
 {
+    /* No match if pos > endpos. */
+    if (state->ptr > state->end) {
+        return 0;
+    }
     if (state->charsize == 1)
         return sre_ucs1_match(state, pattern, 1);
     if (state->charsize == 2)

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to