diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-09-23-25-42.gh-issue-128679.Uhblds.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-09-23-25-42.gh-issue-128679.Uhblds.rst new file mode 100644 index 00000000000000..b510b19a5c5652 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-09-23-25-42.gh-issue-128679.Uhblds.rst @@ -0,0 +1 @@ +Fix race condition in tracemalloc causing abort. diff --git a/Python/tracemalloc.c b/Python/tracemalloc.c index f661d69c0312fa..a48b0fcf4817b3 100644 --- a/Python/tracemalloc.c +++ b/Python/tracemalloc.c @@ -538,11 +538,20 @@ tracemalloc_alloc(int use_calloc, void *ctx, size_t nelem, size_t elsize) return NULL; TABLES_LOCK(); - if (ADD_TRACE(ptr, nelem * elsize) < 0) { - /* Failed to allocate a trace for the new memory block */ - TABLES_UNLOCK(); - alloc->free(alloc->ctx, ptr); - return NULL; + /* This operation can be executed outside of GIL protection due to + allocations needed for an initial PyGILState_Ensure(). Because of this, + tracing may be turned off concurrently in another thread which currently + holds the GIL between the initial check of `tracing` variable and here. + This is a sanity check to account for this possibility and avoid a + potential abort where it can be handled gracefully instead. + See gh-128679. */ + if (tracemalloc_config.tracing) { + if (ADD_TRACE(ptr, nelem * elsize) < 0) { + /* Failed to allocate a trace for the new memory block */ + TABLES_UNLOCK(); + alloc->free(alloc->ctx, ptr); + return NULL; + } } TABLES_UNLOCK(); return ptr; @@ -963,8 +972,11 @@ _PyTraceMalloc_Stop(void) if (!tracemalloc_config.tracing) return; - /* stop tracing Python memory allocations */ + /* stop tracing Python memory allocations, + but not while something might be in the middle of an operation */ + TABLES_LOCK(); tracemalloc_config.tracing = 0; + TABLES_UNLOCK(); /* unregister the hook on memory allocators */ #ifdef TRACE_RAW_MALLOC @@ -1317,6 +1329,12 @@ PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr, gil_state = PyGILState_Ensure(); + if (!tracemalloc_config.tracing) { + /* tracing may have been turned off as we were acquiring the GIL */ + PyGILState_Release(gil_state); + return -2; + } + TABLES_LOCK(); res = tracemalloc_add_trace(domain, ptr, size); TABLES_UNLOCK();