mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	gh-104690: thread_run() checks for tstate dangling pointer (#109056)
thread_run() of _threadmodule.c now calls _PyThreadState_CheckConsistency() to check if tstate is a dangling pointer when Python is built in debug mode. Rename ceval_gil.c is_tstate_valid() to _PyThreadState_CheckConsistency() to reuse it in _threadmodule.c.
This commit is contained in:
		
							parent
							
								
									b0edf3b98e
								
							
						
					
					
						commit
						f63d37877a
					
				
					 4 changed files with 35 additions and 20 deletions
				
			
		|  | @ -67,6 +67,10 @@ _Py_ThreadCanHandleSignals(PyInterpreterState *interp) | ||||||
| extern _Py_thread_local PyThreadState *_Py_tss_tstate; | extern _Py_thread_local PyThreadState *_Py_tss_tstate; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifndef NDEBUG | ||||||
|  | extern int _PyThreadState_CheckConsistency(PyThreadState *tstate); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| // Export for most shared extensions, used via _PyThreadState_GET() static
 | // Export for most shared extensions, used via _PyThreadState_GET() static
 | ||||||
| // inline function.
 | // inline function.
 | ||||||
| PyAPI_FUNC(PyThreadState *) _PyThreadState_GetCurrent(void); | PyAPI_FUNC(PyThreadState *) _PyThreadState_GetCurrent(void); | ||||||
|  |  | ||||||
|  | @ -1074,9 +1074,12 @@ static void | ||||||
| thread_run(void *boot_raw) | thread_run(void *boot_raw) | ||||||
| { | { | ||||||
|     struct bootstate *boot = (struct bootstate *) boot_raw; |     struct bootstate *boot = (struct bootstate *) boot_raw; | ||||||
|     PyThreadState *tstate; |     PyThreadState *tstate = boot->tstate; | ||||||
|  | 
 | ||||||
|  |     // gh-104690: If Python is being finalized and PyInterpreterState_Delete()
 | ||||||
|  |     // was called, tstate becomes a dangling pointer.
 | ||||||
|  |     assert(_PyThreadState_CheckConsistency(tstate)); | ||||||
| 
 | 
 | ||||||
|     tstate = boot->tstate; |  | ||||||
|     _PyThreadState_Bind(tstate); |     _PyThreadState_Bind(tstate); | ||||||
|     PyEval_AcquireThread(tstate); |     PyEval_AcquireThread(tstate); | ||||||
|     tstate->interp->threads.count++; |     tstate->interp->threads.count++; | ||||||
|  |  | ||||||
|  | @ -163,16 +163,6 @@ UNSIGNAL_ASYNC_EXC(PyInterpreterState *interp) | ||||||
|     COMPUTE_EVAL_BREAKER(interp, ceval, ceval2); |     COMPUTE_EVAL_BREAKER(interp, ceval, ceval2); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #ifndef NDEBUG |  | ||||||
| /* Ensure that tstate is valid */ |  | ||||||
| static int |  | ||||||
| is_tstate_valid(PyThreadState *tstate) |  | ||||||
| { |  | ||||||
|     assert(!_PyMem_IsPtrFreed(tstate)); |  | ||||||
|     assert(!_PyMem_IsPtrFreed(tstate->interp)); |  | ||||||
|     return 1; |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Implementation of the Global Interpreter Lock (GIL). |  * Implementation of the Global Interpreter Lock (GIL). | ||||||
|  | @ -325,7 +315,7 @@ drop_gil(struct _ceval_state *ceval, PyThreadState *tstate) | ||||||
|         /* Not switched yet => wait */ |         /* Not switched yet => wait */ | ||||||
|         if (((PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) == tstate) |         if (((PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) == tstate) | ||||||
|         { |         { | ||||||
|             assert(is_tstate_valid(tstate)); |             assert(_PyThreadState_CheckConsistency(tstate)); | ||||||
|             RESET_GIL_DROP_REQUEST(tstate->interp); |             RESET_GIL_DROP_REQUEST(tstate->interp); | ||||||
|             /* NOTE: if COND_WAIT does not atomically start waiting when
 |             /* NOTE: if COND_WAIT does not atomically start waiting when
 | ||||||
|                releasing the mutex, another thread can run through, take |                releasing the mutex, another thread can run through, take | ||||||
|  | @ -386,7 +376,7 @@ take_gil(PyThreadState *tstate) | ||||||
|         PyThread_exit_thread(); |         PyThread_exit_thread(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     assert(is_tstate_valid(tstate)); |     assert(_PyThreadState_CheckConsistency(tstate)); | ||||||
|     PyInterpreterState *interp = tstate->interp; |     PyInterpreterState *interp = tstate->interp; | ||||||
|     struct _ceval_state *ceval = &interp->ceval; |     struct _ceval_state *ceval = &interp->ceval; | ||||||
|     struct _gil_runtime_state *gil = ceval->gil; |     struct _gil_runtime_state *gil = ceval->gil; | ||||||
|  | @ -427,7 +417,7 @@ take_gil(PyThreadState *tstate) | ||||||
|                 } |                 } | ||||||
|                 PyThread_exit_thread(); |                 PyThread_exit_thread(); | ||||||
|             } |             } | ||||||
|             assert(is_tstate_valid(tstate)); |             assert(_PyThreadState_CheckConsistency(tstate)); | ||||||
| 
 | 
 | ||||||
|             SET_GIL_DROP_REQUEST(interp); |             SET_GIL_DROP_REQUEST(interp); | ||||||
|             drop_requested = 1; |             drop_requested = 1; | ||||||
|  | @ -466,7 +456,7 @@ take_gil(PyThreadState *tstate) | ||||||
|         drop_gil(ceval, tstate); |         drop_gil(ceval, tstate); | ||||||
|         PyThread_exit_thread(); |         PyThread_exit_thread(); | ||||||
|     } |     } | ||||||
|     assert(is_tstate_valid(tstate)); |     assert(_PyThreadState_CheckConsistency(tstate)); | ||||||
| 
 | 
 | ||||||
|     if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) { |     if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) { | ||||||
|         RESET_GIL_DROP_REQUEST(interp); |         RESET_GIL_DROP_REQUEST(interp); | ||||||
|  | @ -679,7 +669,7 @@ PyEval_AcquireThread(PyThreadState *tstate) | ||||||
| void | void | ||||||
| PyEval_ReleaseThread(PyThreadState *tstate) | PyEval_ReleaseThread(PyThreadState *tstate) | ||||||
| { | { | ||||||
|     assert(is_tstate_valid(tstate)); |     assert(_PyThreadState_CheckConsistency(tstate)); | ||||||
| 
 | 
 | ||||||
|     PyThreadState *new_tstate = _PyThreadState_SwapNoGIL(NULL); |     PyThreadState *new_tstate = _PyThreadState_SwapNoGIL(NULL); | ||||||
|     if (new_tstate != tstate) { |     if (new_tstate != tstate) { | ||||||
|  | @ -877,7 +867,7 @@ Py_AddPendingCall(int (*func)(void *), void *arg) | ||||||
| static int | static int | ||||||
| handle_signals(PyThreadState *tstate) | handle_signals(PyThreadState *tstate) | ||||||
| { | { | ||||||
|     assert(is_tstate_valid(tstate)); |     assert(_PyThreadState_CheckConsistency(tstate)); | ||||||
|     if (!_Py_ThreadCanHandleSignals(tstate->interp)) { |     if (!_Py_ThreadCanHandleSignals(tstate->interp)) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  | @ -983,7 +973,7 @@ void | ||||||
| _Py_FinishPendingCalls(PyThreadState *tstate) | _Py_FinishPendingCalls(PyThreadState *tstate) | ||||||
| { | { | ||||||
|     assert(PyGILState_Check()); |     assert(PyGILState_Check()); | ||||||
|     assert(is_tstate_valid(tstate)); |     assert(_PyThreadState_CheckConsistency(tstate)); | ||||||
| 
 | 
 | ||||||
|     if (make_pending_calls(tstate->interp) < 0) { |     if (make_pending_calls(tstate->interp) < 0) { | ||||||
|         PyObject *exc = _PyErr_GetRaisedException(tstate); |         PyObject *exc = _PyErr_GetRaisedException(tstate); | ||||||
|  | @ -1024,7 +1014,7 @@ Py_MakePendingCalls(void) | ||||||
|     assert(PyGILState_Check()); |     assert(PyGILState_Check()); | ||||||
| 
 | 
 | ||||||
|     PyThreadState *tstate = _PyThreadState_GET(); |     PyThreadState *tstate = _PyThreadState_GET(); | ||||||
|     assert(is_tstate_valid(tstate)); |     assert(_PyThreadState_CheckConsistency(tstate)); | ||||||
| 
 | 
 | ||||||
|     /* Only execute pending calls on the main thread. */ |     /* Only execute pending calls on the main thread. */ | ||||||
|     if (!_Py_IsMainThread() || !_Py_IsMainInterpreter(tstate->interp)) { |     if (!_Py_IsMainThread() || !_Py_IsMainInterpreter(tstate->interp)) { | ||||||
|  |  | ||||||
|  | @ -2890,6 +2890,24 @@ _PyThreadState_PopFrame(PyThreadState *tstate, _PyInterpreterFrame * frame) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | #ifndef NDEBUG | ||||||
|  | // Check that a Python thread state valid. In practice, this function is used
 | ||||||
|  | // on a Python debug build to check if 'tstate' is a dangling pointer, if the
 | ||||||
|  | // PyThreadState memory has been freed.
 | ||||||
|  | //
 | ||||||
|  | // Usage:
 | ||||||
|  | //
 | ||||||
|  | //     assert(_PyThreadState_CheckConsistency(tstate));
 | ||||||
|  | int | ||||||
|  | _PyThreadState_CheckConsistency(PyThreadState *tstate) | ||||||
|  | { | ||||||
|  |     assert(!_PyMem_IsPtrFreed(tstate)); | ||||||
|  |     assert(!_PyMem_IsPtrFreed(tstate->interp)); | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Victor Stinner
						Victor Stinner