| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent b49e902 commit 49da170
10 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -907,6 +907,35 @@ always available. | |||
| 907 | 907 | It is not guaranteed to exist in all implementations of Python. | |
| 908 | 908 | ||
| 909 | 909 | ||
| 910 | + .. function:: getobjects(limit[, type]) | ||
| 911 | + | ||
| 912 | + This function only exists if CPython was built using the | ||
| 913 | + specialized configure option :option:`--with-trace-refs`. | ||
| 914 | + It is intended only for debugging garbage-collection issues. | ||
| 915 | + | ||
| 916 | + Return a list of up to *limit* dynamically allocated Python objects. | ||
| 917 | + If *type* is given, only objects of that exact type (not subtypes) | ||
| 918 | + are included. | ||
| 919 | + | ||
| 920 | + Objects from the list are not safe to use. | ||
| 921 | + Specifically, the result will include objects from all interpreters that | ||
| 922 | + share their object allocator state (that is, ones created with | ||
| 923 | + :c:member:`PyInterpreterConfig.use_main_obmalloc` set to 1 | ||
| 924 | + or using :c:func:`Py_NewInterpreter`, and the | ||
| 925 | + :ref:`main interpreter <sub-interpreter-support>`). | ||
| 926 | + Mixing objects from different interpreters may lead to crashes | ||
| 927 | + or other unexpected behavior. | ||
| 928 | + | ||
| 929 | + .. impl-detail:: | ||
| 930 | + | ||
| 931 | + This function should be used for specialized purposes only. | ||
| 932 | + It is not guaranteed to exist in all implementations of Python. | ||
| 933 | + | ||
| 934 | + .. versionchanged:: next | ||
| 935 | + | ||
| 936 | + The result may include objects from other interpreters. | ||
| 937 | + | ||
| 938 | + | ||
| 910 | 939 | .. function:: getprofile() | |
| 911 | 940 | ||
| 912 | 941 | .. index:: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -459,7 +459,7 @@ Debug options | |||
| 459 | 459 | Effects: | |
| 460 | 460 | ||
| 461 | 461 | * Define the ``Py_TRACE_REFS`` macro. | |
| 462 | - * Add :func:`!sys.getobjects` function. | ||
| 462 | + * Add :func:`sys.getobjects` function. | ||
| 463 | 463 | * Add :envvar:`PYTHONDUMPREFS` environment variable. | |
| 464 | 464 | ||
| 465 | 465 | This build is not ABI compatible with release build (default build) or debug | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2301,3 +2301,14 @@ email | |||
| 2301 | 2301 | check if the *strict* paramater is available. | |
| 2302 | 2302 | (Contributed by Thomas Dwyer and Victor Stinner for :gh:`102988` to improve | |
| 2303 | 2303 | the CVE-2023-27043 fix.) | |
| 2304 | + | ||
| 2305 | + | ||
| 2306 | + Notable changes in 3.12.8 | ||
| 2307 | + ========================= | ||
| 2308 | + | ||
| 2309 | + sys | ||
| 2310 | + --- | ||
| 2311 | + | ||
| 2312 | + * The previously undocumented special function :func:`sys.getobjects`, | ||
| 2313 | + which only exists in specialized builds of Python, may now return objects | ||
| 2314 | + from other interpreters than the one it's called in. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -24,7 +24,13 @@ struct _py_object_state { | |||
| 24 | 24 | * together via the _ob_prev and _ob_next members of a PyObject, which | |
| 25 | 25 | * exist only in a Py_TRACE_REFS build. | |
| 26 | 26 | */ | |
| 27 | - PyObject refchain; | ||
| 27 | + PyObject *refchain; | ||
| 28 | + /* In most cases, refchain points to _refchain_obj. | ||
| 29 | + * In sub-interpreters that share objmalloc state with the main interp, | ||
| 30 | + * refchain points to the main interpreter's _refchain_obj, and their own | ||
| 31 | + * _refchain_obj is unused. | ||
| 32 | + */ | ||
| 33 | + PyObject _refchain_obj; | ||
| 28 | 34 | #endif | |
| 29 | 35 | int _not_used; | |
| 30 | 36 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -132,15 +132,8 @@ extern PyTypeObject _PyExc_MemoryError; | |||
| 132 | 132 | .context_ver = 1, \ | |
| 133 | 133 | } | |
| 134 | 134 | ||
| 135 | - #ifdef Py_TRACE_REFS | ||
| 136 | - # define _py_object_state_INIT(INTERP) \ | ||
| 137 | - { \ | ||
| 138 | - .refchain = {&INTERP.object_state.refchain, &INTERP.object_state.refchain}, \ | ||
| 139 | - } | ||
| 140 | - #else | ||
| 141 | 135 | # define _py_object_state_INIT(INTERP) \ | |
| 142 | 136 | { 0 } | |
| 143 | - #endif | ||
| 144 | 137 | ||
| 145 | 138 | ||
| 146 | 139 | // global objects | |
| Original file line number | Diff line number | Diff 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. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -159,11 +159,27 @@ _PyDebug_PrintTotalRefs(void) { | |||
| 159 | 159 | ||
| 160 | 160 | #ifdef Py_TRACE_REFS | |
| 161 | 161 | ||
| 162 | - #define REFCHAIN(interp) &interp->object_state.refchain | ||
| 162 | + #define REFCHAIN(interp) interp->object_state.refchain | ||
| 163 | + | ||
| 164 | + static inline int | ||
| 165 | + has_own_refchain(PyInterpreterState *interp) | ||
| 166 | + { | ||
| 167 | + if (interp->feature_flags & Py_RTFLAGS_USE_MAIN_OBMALLOC) { | ||
| 168 | + return (_Py_IsMainInterpreter(interp) | ||
| 169 | + || _PyInterpreterState_Main() == NULL); | ||
| 170 | + } | ||
| 171 | + return 1; | ||
| 172 | + } | ||
| 163 | 173 | ||
| 164 | 174 | static inline void | |
| 165 | 175 | init_refchain(PyInterpreterState *interp) | |
| 166 | 176 | { | |
| 177 | + if (!has_own_refchain(interp)) { | ||
| 178 | + // Legacy subinterpreters share a refchain with the main interpreter. | ||
| 179 | + REFCHAIN(interp) = REFCHAIN(_PyInterpreterState_Main()); | ||
| 180 | + return; | ||
| 181 | + } | ||
| 182 | + REFCHAIN(interp) = &interp->object_state._refchain_obj; | ||
| 167 | 183 | PyObject *refchain = REFCHAIN(interp); | |
| 168 | 184 | refchain->_ob_prev = refchain; | |
| 169 | 185 | refchain->_ob_next = refchain; | |
@@ -2010,9 +2026,7 @@ void | |||
| 2010 | 2026 | _PyObject_InitState(PyInterpreterState *interp) | |
| 2011 | 2027 | { | |
| 2012 | 2028 | #ifdef Py_TRACE_REFS | |
| 2013 | - if (!_Py_IsMainInterpreter(interp)) { | ||
| 2014 | - init_refchain(interp); | ||
| 2015 | - } | ||
| 2029 | + init_refchain(interp); | ||
| 2016 | 2030 | #endif | |
| 2017 | 2031 | } | |
| 2018 | 2032 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -287,13 +287,37 @@ hashtable_unicode_compare(const void *key1, const void *key2) | |||
| 287 | 287 | } | |
| 288 | 288 | } | |
| 289 | 289 | ||
| 290 | + /* Return true if this interpreter should share the main interpreter's | ||
| 291 | + intern_dict. That's important for interpreters which load basic | ||
| 292 | + single-phase init extension modules (m_size == -1). There could be interned | ||
| 293 | + immortal strings that are shared between interpreters, due to the | ||
| 294 | + PyDict_Update(mdict, m_copy) call in import_find_extension(). | ||
| 295 | + | ||
| 296 | + It's not safe to deallocate those strings until all interpreters that | ||
| 297 | + potentially use them are freed. By storing them in the main interpreter, we | ||
| 298 | + ensure they get freed after all other interpreters are freed. | ||
| 299 | + */ | ||
| 300 | + static bool | ||
| 301 | + has_shared_intern_dict(PyInterpreterState *interp) | ||
| 302 | + { | ||
| 303 | + PyInterpreterState *main_interp = _PyInterpreterState_Main(); | ||
| 304 | + return interp != main_interp && interp->feature_flags & Py_RTFLAGS_USE_MAIN_OBMALLOC; | ||
| 305 | + } | ||
| 306 | + | ||
| 290 | 307 | static int | |
| 291 | 308 | init_interned_dict(PyInterpreterState *interp) | |
| 292 | 309 | { | |
| 293 | 310 | assert(get_interned_dict(interp) == NULL); | |
| 294 | - PyObject *interned = interned = PyDict_New(); | ||
| 295 | - if (interned == NULL) { | ||
| 296 | - return -1; | ||
| 311 | + PyObject *interned; | ||
| 312 | + if (has_shared_intern_dict(interp)) { | ||
| 313 | + interned = get_interned_dict(_PyInterpreterState_Main()); | ||
| 314 | + Py_INCREF(interned); | ||
| 315 | + } | ||
| 316 | + else { | ||
| 317 | + interned = PyDict_New(); | ||
| 318 | + if (interned == NULL) { | ||
| 319 | + return -1; | ||
| 320 | + } | ||
| 297 | 321 | } | |
| 298 | 322 | _Py_INTERP_CACHED_OBJECT(interp, interned_strings) = interned; | |
| 299 | 323 | return 0; | |
@@ -304,7 +328,10 @@ clear_interned_dict(PyInterpreterState *interp) | |||
| 304 | 328 | { | |
| 305 | 329 | PyObject *interned = get_interned_dict(interp); | |
| 306 | 330 | if (interned != NULL) { | |
| 307 | - PyDict_Clear(interned); | ||
| 331 | + if (!has_shared_intern_dict(interp)) { | ||
| 332 | + // only clear if the dict belongs to this interpreter | ||
| 333 | + PyDict_Clear(interned); | ||
| 334 | + } | ||
| 308 | 335 | Py_DECREF(interned); | |
| 309 | 336 | _Py_INTERP_CACHED_OBJECT(interp, interned_strings) = NULL; | |
| 310 | 337 | } | |
@@ -15152,6 +15179,13 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp) | |||
| 15152 | 15179 | } | |
| 15153 | 15180 | assert(PyDict_CheckExact(interned)); | |
| 15154 | 15181 | ||
| 15182 | + if (has_shared_intern_dict(interp)) { | ||
| 15183 | + // the dict doesn't belong to this interpreter, skip the debug | ||
| 15184 | + // checks on it and just clear the pointer to it | ||
| 15185 | + clear_interned_dict(interp); | ||
| 15186 | + return; | ||
| 15187 | + } | ||
| 15188 | + | ||
| 15155 | 15189 | #ifdef INTERNED_STATS | |
| 15156 | 15190 | fprintf(stderr, "releasing %zd interned strings\n", | |
| 15157 | 15191 | PyDict_GET_SIZE(interned)); | |
@@ -15670,8 +15704,10 @@ _PyUnicode_Fini(PyInterpreterState *interp) | |||
| 15670 | 15704 | { | |
| 15671 | 15705 | struct _Py_unicode_state *state = &interp->unicode; | |
| 15672 | 15706 | ||
| 15673 | - // _PyUnicode_ClearInterned() must be called before _PyUnicode_Fini() | ||
| 15674 | - assert(get_interned_dict(interp) == NULL); | ||
| 15707 | + if (!has_shared_intern_dict(interp)) { | ||
| 15708 | + // _PyUnicode_ClearInterned() must be called before _PyUnicode_Fini() | ||
| 15709 | + assert(get_interned_dict(interp) == NULL); | ||
| 15710 | + } | ||
| 15675 | 15711 | ||
| 15676 | 15712 | _PyUnicode_FiniEncodings(&state->fs_codec); | |
| 15677 | 15713 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -650,6 +650,10 @@ pycore_create_interpreter(_PyRuntimeState *runtime, | |||
| 650 | 650 | return status; | |
| 651 | 651 | } | |
| 652 | 652 | ||
| 653 | + // This could be done in init_interpreter() (in pystate.c) if it | ||
| 654 | + // didn't depend on interp->feature_flags being set already. | ||
| 655 | + _PyObject_InitState(interp); | ||
| 656 | + | ||
| 653 | 657 | PyThreadState *tstate = _PyThreadState_New(interp); | |
| 654 | 658 | if (tstate == NULL) { | |
| 655 | 659 | return _PyStatus_ERR("can't make first thread"); | |
@@ -2103,6 +2107,10 @@ new_interpreter(PyThreadState **tstate_p, const PyInterpreterConfig *config) | |||
| 2103 | 2107 | goto error; | |
| 2104 | 2108 | } | |
| 2105 | 2109 | ||
| 2110 | + // This could be done in init_interpreter() (in pystate.c) if it | ||
| 2111 | + // didn't depend on interp->feature_flags being set already. | ||
| 2112 | + _PyObject_InitState(interp); | ||
| 2113 | + | ||
| 2106 | 2114 | status = init_interp_create_gil(tstate, config->gil); | |
| 2107 | 2115 | if (_PyStatus_EXCEPTION(status)) { | |
| 2108 | 2116 | goto error; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -686,7 +686,9 @@ init_interpreter(PyInterpreterState *interp, | |||
| 686 | 686 | _obmalloc_pools_INIT(interp->obmalloc.pools); | |
| 687 | 687 | memcpy(&interp->obmalloc.pools.used, temp, sizeof(temp)); | |
| 688 | 688 | } | |
| 689 | - _PyObject_InitState(interp); | ||
| 689 | + | ||
| 690 | + // We would call _PyObject_InitState() at this point | ||
| 691 | + // if interp->feature_flags were alredy set. | ||
| 690 | 692 | ||
| 691 | 693 | _PyEval_InitState(interp, pending_lock); | |
| 692 | 694 | _PyGC_InitState(&interp->gc); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments