Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 98b2ed7

Browse files
authored
gh-116510: Fix crash due to shared immortal interned strings. (gh-124646)
1 parent 65f1237 commit 98b2ed7

File tree

2 files changed

+47
-6
lines changed

2 files changed

+47
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fix a crash caused by immortal interned strings being shared between
2+
sub-interpreters that use basic single-phase init. In that case, the string
3+
can be used by an interpreter that outlives the interpreter that created and
4+
interned it. For interpreters that share obmalloc state, also share the
5+
interned dict with the main interpreter.

Objects/unicodeobject.c

+42-6
Original file line numberDiff line numberDiff line change
@@ -282,13 +282,37 @@ hashtable_unicode_compare(const void *key1, const void *key2)
282282
}
283283
}
284284

285+
/* Return true if this interpreter should share the main interpreter's
286+
intern_dict. That's important for interpreters which load basic
287+
single-phase init extension modules (m_size == -1). There could be interned
288+
immortal strings that are shared between interpreters, due to the
289+
PyDict_Update(mdict, m_copy) call in import_find_extension().
290+
291+
It's not safe to deallocate those strings until all interpreters that
292+
potentially use them are freed. By storing them in the main interpreter, we
293+
ensure they get freed after all other interpreters are freed.
294+
*/
295+
static bool
296+
has_shared_intern_dict(PyInterpreterState *interp)
297+
{
298+
PyInterpreterState *main_interp = _PyInterpreterState_Main();
299+
return interp != main_interp && interp->feature_flags & Py_RTFLAGS_USE_MAIN_OBMALLOC;
300+
}
301+
285302
static int
286303
init_interned_dict(PyInterpreterState *interp)
287304
{
288305
assert(get_interned_dict(interp) == NULL);
289-
PyObject *interned = interned = PyDict_New();
290-
if (interned == NULL) {
291-
return -1;
306+
PyObject *interned;
307+
if (has_shared_intern_dict(interp)) {
308+
interned = get_interned_dict(_PyInterpreterState_Main());
309+
Py_INCREF(interned);
310+
}
311+
else {
312+
interned = PyDict_New();
313+
if (interned == NULL) {
314+
return -1;
315+
}
292316
}
293317
_Py_INTERP_CACHED_OBJECT(interp, interned_strings) = interned;
294318
return 0;
@@ -299,7 +323,10 @@ clear_interned_dict(PyInterpreterState *interp)
299323
{
300324
PyObject *interned = get_interned_dict(interp);
301325
if (interned != NULL) {
302-
PyDict_Clear(interned);
326+
if (!has_shared_intern_dict(interp)) {
327+
// only clear if the dict belongs to this interpreter
328+
PyDict_Clear(interned);
329+
}
303330
Py_DECREF(interned);
304331
_Py_INTERP_CACHED_OBJECT(interp, interned_strings) = NULL;
305332
}
@@ -15618,6 +15645,13 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp)
1561815645
}
1561915646
assert(PyDict_CheckExact(interned));
1562015647

15648+
if (has_shared_intern_dict(interp)) {
15649+
// the dict doesn't belong to this interpreter, skip the debug
15650+
// checks on it and just clear the pointer to it
15651+
clear_interned_dict(interp);
15652+
return;
15653+
}
15654+
1562115655
#ifdef INTERNED_STATS
1562215656
fprintf(stderr, "releasing %zd interned strings\n",
1562315657
PyDict_GET_SIZE(interned));
@@ -16126,8 +16160,10 @@ _PyUnicode_Fini(PyInterpreterState *interp)
1612616160
{
1612716161
struct _Py_unicode_state *state = &interp->unicode;
1612816162

16129-
// _PyUnicode_ClearInterned() must be called before _PyUnicode_Fini()
16130-
assert(get_interned_dict(interp) == NULL);
16163+
if (!has_shared_intern_dict(interp)) {
16164+
// _PyUnicode_ClearInterned() must be called before _PyUnicode_Fini()
16165+
assert(get_interned_dict(interp) == NULL);
16166+
}
1613116167

1613216168
_PyUnicode_FiniEncodings(&state->fs_codec);
1613316169

0 commit comments

Comments
 (0)