@@ -15296,6 +15296,42 @@ PyUnicode_InternFromString(const char *cp)
15296
15296
return s ;
15297
15297
}
15298
15298
15299
+ void
15300
+ clear_interned_dict_legacy (PyInterpreterState * interp )
15301
+ {
15302
+ // See GH-116510 for why this extra care is needed. Immortal interned
15303
+ // strings can be shared between subinterpreters in this case and we
15304
+ // can't safely free them without a potential use-after-free crash.
15305
+ PyObject * interned = get_interned_dict (interp );
15306
+ Py_ssize_t pos = 0 ;
15307
+ PyObject * s , * ignored_value ;
15308
+ while (PyDict_Next (interned , & pos , & s , & ignored_value )) {
15309
+ assert (PyUnicode_IS_READY (s ));
15310
+ switch (PyUnicode_CHECK_INTERNED (s )) {
15311
+ case SSTATE_INTERNED_IMMORTAL :
15312
+ case SSTATE_INTERNED_IMMORTAL_STATIC :
15313
+ // immortal strings leak since we can't safely free them
15314
+ break ;
15315
+ case SSTATE_INTERNED_MORTAL :
15316
+ // Restore 2 references held by the interned dict; these will
15317
+ // be decref'd by clear_interned_dict's PyDict_Clear.
15318
+ Py_SET_REFCNT (s , Py_REFCNT (s ) + 2 );
15319
+ #ifdef Py_REF_DEBUG
15320
+ /* let's be pedantic with the ref total */
15321
+ _Py_IncRefTotal (_PyThreadState_GET ());
15322
+ _Py_IncRefTotal (_PyThreadState_GET ());
15323
+ #endif
15324
+ break ;
15325
+ case SSTATE_NOT_INTERNED :
15326
+ /* fall through */
15327
+ default :
15328
+ Py_UNREACHABLE ();
15329
+ }
15330
+ _PyUnicode_STATE (s ).interned = SSTATE_NOT_INTERNED ;
15331
+ }
15332
+ PyDict_Clear (interned );
15333
+ _Py_INTERP_CACHED_OBJECT (interp , interned_strings ) = NULL ;
15334
+ }
15299
15335
15300
15336
void
15301
15337
_PyUnicode_ClearInterned (PyInterpreterState * interp )
@@ -15306,6 +15342,11 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp)
15306
15342
}
15307
15343
assert (PyDict_CheckExact (interned ));
15308
15344
15345
+ if (interp -> leak_interned_strings ) {
15346
+ clear_interned_dict_legacy (interp );
15347
+ return ;
15348
+ }
15349
+
15309
15350
#ifdef INTERNED_STATS
15310
15351
fprintf (stderr , "releasing %zd interned strings\n" ,
15311
15352
PyDict_GET_SIZE (interned ));
0 commit comments