mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	gh-137400: Fix a crash when disabling profiling across all threads (gh-137471)
The `PyEval_SetProfileAllThreads` function and other related functions had a race condition on `tstate->c_profilefunc` that could lead to a crash when disable profiling or tracing on all threads while another thread is starting to profile or trace a a call. There are still potential crashes when threads exit concurrently with profiling or tracing be enabled/disabled across all threads.
This commit is contained in:
		
							parent
							
								
									deb0020e3d
								
							
						
					
					
						commit
						362692852f
					
				
					 3 changed files with 46 additions and 2 deletions
				
			
		| 
						 | 
					@ -194,6 +194,40 @@ def during_threads(self):
 | 
				
			||||||
        self.set = not self.set
 | 
					        self.set = not self.set
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@threading_helper.requires_working_threading()
 | 
				
			||||||
 | 
					class SetProfileAllMultiThreaded(TestCase):
 | 
				
			||||||
 | 
					    def test_profile_all_threads(self):
 | 
				
			||||||
 | 
					        done = threading.Event()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def func():
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def bg_thread():
 | 
				
			||||||
 | 
					            while not done.is_set():
 | 
				
			||||||
 | 
					                func()
 | 
				
			||||||
 | 
					                func()
 | 
				
			||||||
 | 
					                func()
 | 
				
			||||||
 | 
					                func()
 | 
				
			||||||
 | 
					                func()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def my_profile(frame, event, arg):
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bg_threads = []
 | 
				
			||||||
 | 
					        for i in range(10):
 | 
				
			||||||
 | 
					            t = threading.Thread(target=bg_thread)
 | 
				
			||||||
 | 
					            t.start()
 | 
				
			||||||
 | 
					            bg_threads.append(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in range(100):
 | 
				
			||||||
 | 
					            threading.setprofile_all_threads(my_profile)
 | 
				
			||||||
 | 
					            threading.setprofile_all_threads(None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        done.set()
 | 
				
			||||||
 | 
					        for t in bg_threads:
 | 
				
			||||||
 | 
					            t.join()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TraceBuf:
 | 
					class TraceBuf:
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        self.traces = []
 | 
					        self.traces = []
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					Fix a crash in the :term:`free threading` build when disabling profiling or tracing
 | 
				
			||||||
 | 
					across all threads with :c:func:`PyEval_SetProfileAllThreads` or
 | 
				
			||||||
 | 
					:c:func:`PyEval_SetTraceAllThreads` or their Python equivalents
 | 
				
			||||||
 | 
					:func:`threading.settrace_all_threads` and :func:`threading.setprofile_all_threads`.
 | 
				
			||||||
| 
						 | 
					@ -477,13 +477,16 @@ setup_profile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg, PyObject
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _PyEval_StopTheWorld(tstate->interp);
 | 
				
			||||||
    int delta = (func != NULL) - (tstate->c_profilefunc != NULL);
 | 
					    int delta = (func != NULL) - (tstate->c_profilefunc != NULL);
 | 
				
			||||||
    tstate->c_profilefunc = func;
 | 
					    tstate->c_profilefunc = func;
 | 
				
			||||||
    *old_profileobj = tstate->c_profileobj;
 | 
					    *old_profileobj = tstate->c_profileobj;
 | 
				
			||||||
    tstate->c_profileobj = Py_XNewRef(arg);
 | 
					    tstate->c_profileobj = Py_XNewRef(arg);
 | 
				
			||||||
    tstate->interp->sys_profiling_threads += delta;
 | 
					    tstate->interp->sys_profiling_threads += delta;
 | 
				
			||||||
    assert(tstate->interp->sys_profiling_threads >= 0);
 | 
					    assert(tstate->interp->sys_profiling_threads >= 0);
 | 
				
			||||||
    return tstate->interp->sys_profiling_threads;
 | 
					    Py_ssize_t profiling_threads = tstate->interp->sys_profiling_threads;
 | 
				
			||||||
 | 
					    _PyEval_StartTheWorld(tstate->interp);
 | 
				
			||||||
 | 
					    return profiling_threads;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int
 | 
					int
 | 
				
			||||||
| 
						 | 
					@ -574,13 +577,16 @@ setup_tracing(PyThreadState *tstate, Py_tracefunc func, PyObject *arg, PyObject
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _PyEval_StopTheWorld(tstate->interp);
 | 
				
			||||||
    int delta = (func != NULL) - (tstate->c_tracefunc != NULL);
 | 
					    int delta = (func != NULL) - (tstate->c_tracefunc != NULL);
 | 
				
			||||||
    tstate->c_tracefunc = func;
 | 
					    tstate->c_tracefunc = func;
 | 
				
			||||||
    *old_traceobj = tstate->c_traceobj;
 | 
					    *old_traceobj = tstate->c_traceobj;
 | 
				
			||||||
    tstate->c_traceobj = Py_XNewRef(arg);
 | 
					    tstate->c_traceobj = Py_XNewRef(arg);
 | 
				
			||||||
    tstate->interp->sys_tracing_threads += delta;
 | 
					    tstate->interp->sys_tracing_threads += delta;
 | 
				
			||||||
    assert(tstate->interp->sys_tracing_threads >= 0);
 | 
					    assert(tstate->interp->sys_tracing_threads >= 0);
 | 
				
			||||||
    return tstate->interp->sys_tracing_threads;
 | 
					    Py_ssize_t tracing_threads = tstate->interp->sys_tracing_threads;
 | 
				
			||||||
 | 
					    _PyEval_StartTheWorld(tstate->interp);
 | 
				
			||||||
 | 
					    return tracing_threads;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int
 | 
					int
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue