From 956270d08d5c23f59937e2f29f8e0b7f63d68afd Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 16 Jul 2024 15:17:29 +0200 Subject: [PATCH] gh-113993: For string interning, do not rely on (or assert) _Py_IsImmortal (GH-121358) Older stable ABI extensions are allowed to make immortal objects mortal. Instead, use `_PyUnicode_STATE` (`interned` and `statically_allocated`). --- ...2024-07-04-13-23-27.gh-issue-113601.K3RLqp.rst | 2 ++ Objects/unicodeobject.c | 15 ++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2024-07-04-13-23-27.gh-issue-113601.K3RLqp.rst diff --git a/Misc/NEWS.d/next/C API/2024-07-04-13-23-27.gh-issue-113601.K3RLqp.rst b/Misc/NEWS.d/next/C API/2024-07-04-13-23-27.gh-issue-113601.K3RLqp.rst new file mode 100644 index 00000000000..009cc2bf017 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-07-04-13-23-27.gh-issue-113601.K3RLqp.rst @@ -0,0 +1,2 @@ +Removed debug build assertions related to interning strings, which were +falsely triggered by stable ABI extensions. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 394ea888fc9..c2fb135ed68 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -252,7 +252,8 @@ _PyUnicode_InternedSize_Immortal(void) // value, to help detect bugs in optimizations. while (PyDict_Next(dict, &pos, &key, &value)) { - if (_Py_IsImmortal(key)) { + assert(PyUnicode_CHECK_INTERNED(key) != SSTATE_INTERNED_IMMORTAL_STATIC); + if (PyUnicode_CHECK_INTERNED(key) == SSTATE_INTERNED_IMMORTAL) { count++; } } @@ -688,10 +689,14 @@ _PyUnicode_CheckConsistency(PyObject *op, int check_content) /* Check interning state */ #ifdef Py_DEBUG + // Note that we do not check `_Py_IsImmortal(op)`, since stable ABI + // extensions can make immortal strings mortal (but with a high enough + // refcount). + // The other way is extremely unlikely (worth a potential failed assertion + // in a debug build), so we do check `!_Py_IsImmortal(op)`. switch (PyUnicode_CHECK_INTERNED(op)) { case SSTATE_NOT_INTERNED: if (ascii->state.statically_allocated) { - CHECK(_Py_IsImmortal(op)); // This state is for two exceptions: // - strings are currently checked before they're interned // - the 256 one-latin1-character strings @@ -707,11 +712,9 @@ _PyUnicode_CheckConsistency(PyObject *op, int check_content) break; case SSTATE_INTERNED_IMMORTAL: CHECK(!ascii->state.statically_allocated); - CHECK(_Py_IsImmortal(op)); break; case SSTATE_INTERNED_IMMORTAL_STATIC: CHECK(ascii->state.statically_allocated); - CHECK(_Py_IsImmortal(op)); break; default: Py_UNREACHABLE(); @@ -1867,7 +1870,6 @@ static PyObject* get_latin1_char(Py_UCS1 ch) { PyObject *o = LATIN1(ch); - assert(_Py_IsImmortal(o)); return o; } @@ -15352,7 +15354,6 @@ intern_static(PyInterpreterState *interp, PyObject *s /* stolen */) assert(s != NULL); assert(_PyUnicode_CHECK(s)); assert(_PyUnicode_STATE(s).statically_allocated); - assert(_Py_IsImmortal(s)); switch (PyUnicode_CHECK_INTERNED(s)) { case SSTATE_NOT_INTERNED: @@ -15493,7 +15494,7 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */, { PyObject *r = (PyObject *)_Py_hashtable_get(INTERNED_STRINGS, s); if (r != NULL) { - assert(_Py_IsImmortal(r)); + assert(_PyUnicode_STATE(r).statically_allocated); assert(r != s); // r must be statically_allocated; s is not Py_DECREF(s); return Py_NewRef(r);