mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +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; | ||||
| #endif | ||||
| 
 | ||||
| #ifndef NDEBUG | ||||
| extern int _PyThreadState_CheckConsistency(PyThreadState *tstate); | ||||
| #endif | ||||
| 
 | ||||
| // Export for most shared extensions, used via _PyThreadState_GET() static
 | ||||
| // inline function.
 | ||||
| PyAPI_FUNC(PyThreadState *) _PyThreadState_GetCurrent(void); | ||||
|  |  | |||
|  | @ -1074,9 +1074,12 @@ static void | |||
| thread_run(void *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); | ||||
|     PyEval_AcquireThread(tstate); | ||||
|     tstate->interp->threads.count++; | ||||
|  |  | |||
|  | @ -163,16 +163,6 @@ UNSIGNAL_ASYNC_EXC(PyInterpreterState *interp) | |||
|     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). | ||||
|  | @ -325,7 +315,7 @@ drop_gil(struct _ceval_state *ceval, PyThreadState *tstate) | |||
|         /* Not switched yet => wait */ | ||||
|         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); | ||||
|             /* NOTE: if COND_WAIT does not atomically start waiting when
 | ||||
|                releasing the mutex, another thread can run through, take | ||||
|  | @ -386,7 +376,7 @@ take_gil(PyThreadState *tstate) | |||
|         PyThread_exit_thread(); | ||||
|     } | ||||
| 
 | ||||
|     assert(is_tstate_valid(tstate)); | ||||
|     assert(_PyThreadState_CheckConsistency(tstate)); | ||||
|     PyInterpreterState *interp = tstate->interp; | ||||
|     struct _ceval_state *ceval = &interp->ceval; | ||||
|     struct _gil_runtime_state *gil = ceval->gil; | ||||
|  | @ -427,7 +417,7 @@ take_gil(PyThreadState *tstate) | |||
|                 } | ||||
|                 PyThread_exit_thread(); | ||||
|             } | ||||
|             assert(is_tstate_valid(tstate)); | ||||
|             assert(_PyThreadState_CheckConsistency(tstate)); | ||||
| 
 | ||||
|             SET_GIL_DROP_REQUEST(interp); | ||||
|             drop_requested = 1; | ||||
|  | @ -466,7 +456,7 @@ take_gil(PyThreadState *tstate) | |||
|         drop_gil(ceval, tstate); | ||||
|         PyThread_exit_thread(); | ||||
|     } | ||||
|     assert(is_tstate_valid(tstate)); | ||||
|     assert(_PyThreadState_CheckConsistency(tstate)); | ||||
| 
 | ||||
|     if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) { | ||||
|         RESET_GIL_DROP_REQUEST(interp); | ||||
|  | @ -679,7 +669,7 @@ PyEval_AcquireThread(PyThreadState *tstate) | |||
| void | ||||
| PyEval_ReleaseThread(PyThreadState *tstate) | ||||
| { | ||||
|     assert(is_tstate_valid(tstate)); | ||||
|     assert(_PyThreadState_CheckConsistency(tstate)); | ||||
| 
 | ||||
|     PyThreadState *new_tstate = _PyThreadState_SwapNoGIL(NULL); | ||||
|     if (new_tstate != tstate) { | ||||
|  | @ -877,7 +867,7 @@ Py_AddPendingCall(int (*func)(void *), void *arg) | |||
| static int | ||||
| handle_signals(PyThreadState *tstate) | ||||
| { | ||||
|     assert(is_tstate_valid(tstate)); | ||||
|     assert(_PyThreadState_CheckConsistency(tstate)); | ||||
|     if (!_Py_ThreadCanHandleSignals(tstate->interp)) { | ||||
|         return 0; | ||||
|     } | ||||
|  | @ -983,7 +973,7 @@ void | |||
| _Py_FinishPendingCalls(PyThreadState *tstate) | ||||
| { | ||||
|     assert(PyGILState_Check()); | ||||
|     assert(is_tstate_valid(tstate)); | ||||
|     assert(_PyThreadState_CheckConsistency(tstate)); | ||||
| 
 | ||||
|     if (make_pending_calls(tstate->interp) < 0) { | ||||
|         PyObject *exc = _PyErr_GetRaisedException(tstate); | ||||
|  | @ -1024,7 +1014,7 @@ Py_MakePendingCalls(void) | |||
|     assert(PyGILState_Check()); | ||||
| 
 | ||||
|     PyThreadState *tstate = _PyThreadState_GET(); | ||||
|     assert(is_tstate_valid(tstate)); | ||||
|     assert(_PyThreadState_CheckConsistency(tstate)); | ||||
| 
 | ||||
|     /* Only execute pending calls on the main thread. */ | ||||
|     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 | ||||
| } | ||||
| #endif | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Victor Stinner
						Victor Stinner