https://github.com/python/cpython/commit/6b58f9a6a12a3c514a755e5e86dc9b9f36bacb9d
commit: 6b58f9a6a12a3c514a755e5e86dc9b9f36bacb9d
branch: main
author: Gregory P. Smith <[email protected]>
committer: gpshead <[email protected]>
date: 2026-10-31T16:14:26-07:00
summary:

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

gh-160316: Make re.Pattern.match() return None when pos > endpos

Pattern.match(), prefixmatch(), 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

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 0be8d2864219f3d..26c30028665c408 100644
--- a/Lib/test/test_re.py
+++ b/Lib/test/test_re.py
@@ -2428,6 +2428,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..0e52137bde01ed5
--- /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.prefixmatch`,
+:meth:`re.Pattern.fullmatch` and the ``match()`` and ``prefixmatch()`` methods
+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 47c2c4abc3ef24e..8d4b46c0fd14b0e 100644
--- a/Modules/_sre/sre.c
+++ b/Modules/_sre/sre.c
@@ -899,6 +899,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