Skip to content

Commit 724cf80

Browse files
committed
gh-152228: Fix a crash in str.replace under a limited memory case
1 parent 55bc312 commit 724cf80

3 files changed

Lines changed: 26 additions & 9 deletions

File tree

Lib/test/test_str.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,22 @@ def test_replace_id(self):
607607
text = 'abc def'
608608
self.assertIs(text.replace(pattern, pattern), text)
609609

610+
@support.cpython_only
611+
@unittest.skipIf(_testcapi is None, reason="_testcapi is required for this test")
612+
def test_replace_oom(self):
613+
# https://github.com/python/cpython/issues/152228
614+
s1 = "轘" * 4
615+
s2 = "&"
616+
s3 = "&"
617+
assertion = self.assertRaises(MemoryError)
618+
_testcapi.set_nomemory(0, 0)
619+
try:
620+
# No allocations made in the test itself:
621+
with assertion:
622+
s1.replace(s2, s3) # this line used to crash before
623+
finally:
624+
_testcapi.remove_mem_hooks()
625+
610626
def test_repeat_id_preserving(self):
611627
a = '123abc1@'
612628
b = '456zyx-+'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a crash in :meth:`str.replace` under a limited memory situation.

Objects/unicodeobject.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10753,9 +10753,9 @@ replace(PyObject *self, PyObject *str1,
1075310753
}
1075410754

1075510755
done:
10756-
assert(srelease == (sbuf != PyUnicode_DATA(self)));
10757-
assert(release1 == (buf1 != PyUnicode_DATA(str1)));
10758-
assert(release2 == (buf2 != PyUnicode_DATA(str2)));
10756+
assert(srelease == (sbuf != NULL && sbuf != PyUnicode_DATA(self)));
10757+
assert(release1 == (buf1 != NULL && buf1 != PyUnicode_DATA(str1)));
10758+
assert(release2 == (buf2 != NULL && buf2 != PyUnicode_DATA(str2)));
1075910759
if (srelease)
1076010760
PyMem_Free((void *)sbuf);
1076110761
if (release1)
@@ -10767,9 +10767,9 @@ replace(PyObject *self, PyObject *str1,
1076710767

1076810768
nothing:
1076910769
/* nothing to replace; return original string (when possible) */
10770-
assert(srelease == (sbuf != PyUnicode_DATA(self)));
10771-
assert(release1 == (buf1 != PyUnicode_DATA(str1)));
10772-
assert(release2 == (buf2 != PyUnicode_DATA(str2)));
10770+
assert(srelease == (sbuf != NULL && sbuf != PyUnicode_DATA(self)));
10771+
assert(release1 == (buf1 != NULL && buf1 != PyUnicode_DATA(str1)));
10772+
assert(release2 == (buf2 != NULL && buf2 != PyUnicode_DATA(str2)));
1077310773
if (srelease)
1077410774
PyMem_Free((void *)sbuf);
1077510775
if (release1)
@@ -10779,9 +10779,9 @@ replace(PyObject *self, PyObject *str1,
1077910779
return unicode_result_unchanged(self);
1078010780

1078110781
error:
10782-
assert(srelease == (sbuf != PyUnicode_DATA(self)));
10783-
assert(release1 == (buf1 != PyUnicode_DATA(str1)));
10784-
assert(release2 == (buf2 != PyUnicode_DATA(str2)));
10782+
assert(srelease == (sbuf != NULL && sbuf != PyUnicode_DATA(self)));
10783+
assert(release1 == (buf1 != NULL && buf1 != PyUnicode_DATA(str1)));
10784+
assert(release2 == (buf2 != NULL && buf2 != PyUnicode_DATA(str2)));
1078510785
if (srelease)
1078610786
PyMem_Free((void *)sbuf);
1078710787
if (release1)

0 commit comments

Comments
 (0)