Skip to content

Commit c5043dc

Browse files
authored
gh-152228: Fix an assertion failure in str.replace under a limited memory case (#152229)
1 parent 7ccdbab commit c5043dc

3 files changed

Lines changed: 26 additions & 9 deletions

File tree

Lib/test/test_str.py

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

610+
@support.nomemtest
611+
def test_replace_oom(self):
612+
# https://github.com/python/cpython/issues/152228
613+
s1 = "轘" * 4
614+
s2 = "&"
615+
s3 = "&"
616+
assertion = self.assertRaises(MemoryError)
617+
_testcapi.set_nomemory(0, 0)
618+
try:
619+
# No allocations made in the test itself:
620+
with assertion:
621+
s1.replace(s2, s3) # this line used to crash before
622+
finally:
623+
_testcapi.remove_mem_hooks()
624+
610625
def test_repeat_id_preserving(self):
611626
a = '123abc1@'
612627
b = '456zyx-+'
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix an assertion failure when python is built in a debug mode
2+
that happened 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)