mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
[3.13] gh-116510: Fix crash due to shared immortal interned strings. (gh-124646) (#124648)
gh-116510: Fix crash due to shared immortal interned strings. (gh-124646)
(cherry picked from commit 98b2ed7e23
)
Co-authored-by: Neil Schemenauer <nas-github@arctrix.com>
This commit is contained in:
parent
1cd2b97113
commit
dc09a0c67f
2 changed files with 47 additions and 6 deletions
|
@ -0,0 +1,5 @@
|
||||||
|
Fix a crash caused by immortal interned strings being shared between
|
||||||
|
sub-interpreters that use basic single-phase init. In that case, the string
|
||||||
|
can be used by an interpreter that outlives the interpreter that created and
|
||||||
|
interned it. For interpreters that share obmalloc state, also share the
|
||||||
|
interned dict with the main interpreter.
|
|
@ -277,14 +277,38 @@ hashtable_unicode_compare(const void *key1, const void *key2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return true if this interpreter should share the main interpreter's
|
||||||
|
intern_dict. That's important for interpreters which load basic
|
||||||
|
single-phase init extension modules (m_size == -1). There could be interned
|
||||||
|
immortal strings that are shared between interpreters, due to the
|
||||||
|
PyDict_Update(mdict, m_copy) call in import_find_extension().
|
||||||
|
|
||||||
|
It's not safe to deallocate those strings until all interpreters that
|
||||||
|
potentially use them are freed. By storing them in the main interpreter, we
|
||||||
|
ensure they get freed after all other interpreters are freed.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
has_shared_intern_dict(PyInterpreterState *interp)
|
||||||
|
{
|
||||||
|
PyInterpreterState *main_interp = _PyInterpreterState_Main();
|
||||||
|
return interp != main_interp && interp->feature_flags & Py_RTFLAGS_USE_MAIN_OBMALLOC;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
init_interned_dict(PyInterpreterState *interp)
|
init_interned_dict(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
assert(get_interned_dict(interp) == NULL);
|
assert(get_interned_dict(interp) == NULL);
|
||||||
PyObject *interned = interned = PyDict_New();
|
PyObject *interned;
|
||||||
|
if (has_shared_intern_dict(interp)) {
|
||||||
|
interned = get_interned_dict(_PyInterpreterState_Main());
|
||||||
|
Py_INCREF(interned);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
interned = PyDict_New();
|
||||||
if (interned == NULL) {
|
if (interned == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_Py_INTERP_CACHED_OBJECT(interp, interned_strings) = interned;
|
_Py_INTERP_CACHED_OBJECT(interp, interned_strings) = interned;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -294,7 +318,10 @@ clear_interned_dict(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
PyObject *interned = get_interned_dict(interp);
|
PyObject *interned = get_interned_dict(interp);
|
||||||
if (interned != NULL) {
|
if (interned != NULL) {
|
||||||
|
if (!has_shared_intern_dict(interp)) {
|
||||||
|
// only clear if the dict belongs to this interpreter
|
||||||
PyDict_Clear(interned);
|
PyDict_Clear(interned);
|
||||||
|
}
|
||||||
Py_DECREF(interned);
|
Py_DECREF(interned);
|
||||||
_Py_INTERP_CACHED_OBJECT(interp, interned_strings) = NULL;
|
_Py_INTERP_CACHED_OBJECT(interp, interned_strings) = NULL;
|
||||||
}
|
}
|
||||||
|
@ -15306,6 +15333,13 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp)
|
||||||
}
|
}
|
||||||
assert(PyDict_CheckExact(interned));
|
assert(PyDict_CheckExact(interned));
|
||||||
|
|
||||||
|
if (has_shared_intern_dict(interp)) {
|
||||||
|
// the dict doesn't belong to this interpreter, skip the debug
|
||||||
|
// checks on it and just clear the pointer to it
|
||||||
|
clear_interned_dict(interp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef INTERNED_STATS
|
#ifdef INTERNED_STATS
|
||||||
fprintf(stderr, "releasing %zd interned strings\n",
|
fprintf(stderr, "releasing %zd interned strings\n",
|
||||||
PyDict_GET_SIZE(interned));
|
PyDict_GET_SIZE(interned));
|
||||||
|
@ -15827,8 +15861,10 @@ _PyUnicode_Fini(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
struct _Py_unicode_state *state = &interp->unicode;
|
struct _Py_unicode_state *state = &interp->unicode;
|
||||||
|
|
||||||
|
if (!has_shared_intern_dict(interp)) {
|
||||||
// _PyUnicode_ClearInterned() must be called before _PyUnicode_Fini()
|
// _PyUnicode_ClearInterned() must be called before _PyUnicode_Fini()
|
||||||
assert(get_interned_dict(interp) == NULL);
|
assert(get_interned_dict(interp) == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
_PyUnicode_FiniEncodings(&state->fs_codec);
|
_PyUnicode_FiniEncodings(&state->fs_codec);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue