[Lldb-commits] [lldb] cdf67f8 - [lldb][Windows] Lock DebuggerThread::m_process against the debug loop (#236560)

via lldb-commits lldb-commits at lists.llvm.org
Tue Nov 3 10:46:55 PST 2026


Author: Charles Zablit
Date: 2026-11-03T10:46:50-08:00
New Revision: cdf67f811609b54db3e4758bd3eeebe6969db3e4

URL: https://github.com/llvm/llvm-project/commit/cdf67f811609b54db3e4758bd3eeebe6969db3e4
DIFF: https://github.com/llvm/llvm-project/commit/cdf67f811609b54db3e4758bd3eeebe6969db3e4.diff

LOG: [lldb][Windows] Lock DebuggerThread::m_process against the debug loop (#236560)

When the inferior exits before its initial stop, the debug loop resets
`DebuggerThread::m_process` but the launching thread copies it. Both
threads touch the same `shared_ptr`, which corrupts the heap, and
lldb-server dies with `0xC0000374`.

This causes `TestMissingDll.py` to fail with `"Sending vRun packet
failed"`.

This patch guards `m_process` with a mutex.

Tested on Windows with the race window widened: 21 of 800 launches
crashed before, 0 of 4800 after. `TestMissingDll.py` passed 1600 of 1600
runs.

rdar://191149207

Added: 
    

Modified: 
    lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
    lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
index aa88b3a86ba01b..138c852693f01f 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
@@ -168,7 +168,11 @@ lldb::thread_result_t DebuggerThread::DebuggerThreadAttachRoutine(
 Status DebuggerThread::StopDebugging(bool terminate) {
   Status error;
 
-  lldb::pid_t pid = m_process.GetProcessId();
+  // Make a copy of the process, since the termination sequence will reset
+  // DebuggerThread's internal copy and it needs to remain open for the Wait
+  // operation.
+  HostProcess process_copy = GetProcess();
+  lldb::pid_t pid = process_copy.GetProcessId();
 
   Log *log = GetLog(WindowsLog::Process);
   LLDB_LOG(log, "terminate = {0}, inferior={1}.", terminate, pid);
@@ -179,11 +183,7 @@ Status DebuggerThread::StopDebugging(bool terminate) {
   if (!m_is_shutting_down.compare_exchange_strong(expected, true))
     return error;
 
-  // Make a copy of the process, since the termination sequence will reset
-  // DebuggerThread's internal copy and it needs to remain open for the Wait
-  // operation.
-  HostProcess process_copy = m_process;
-  lldb::process_t handle = m_process.GetNativeProcess().GetSystemHandle();
+  lldb::process_t handle = process_copy.GetNativeProcess().GetSystemHandle();
 
   if (terminate) {
     if (handle != nullptr && handle != LLDB_INVALID_PROCESS) {
@@ -215,12 +215,11 @@ Status DebuggerThread::StopDebugging(bool terminate) {
 
   if (!terminate) {
     // Indicate that we want to detach.
-    m_pid_to_detach = GetProcess().GetProcessId();
+    m_pid_to_detach = pid;
 
     // Force a fresh break so that the detach can happen from the debugger
     // thread.
-    if (!::DebugBreakProcess(
-            GetProcess().GetNativeProcess().GetSystemHandle())) {
+    if (!::DebugBreakProcess(handle)) {
       error = Status(::GetLastError(), eErrorTypeWin32);
     }
   }
@@ -257,7 +256,7 @@ void DebuggerThread::ContinueAsyncException(ExceptionResult result) {
 
   Log *log = GetLog(WindowsLog::Process | WindowsLog::Exception);
   LLDB_LOG(log, "broadcasting for inferior process {0}.",
-           m_process.GetProcessId());
+           GetProcess().GetProcessId());
 
   m_exception_pred.SetValue(result, eBroadcastAlways);
 }
@@ -274,7 +273,7 @@ void DebuggerThread::ContinueAsyncDllEvent() {
 }
 
 void DebuggerThread::FreeProcessHandles() {
-  m_process = HostProcess();
+  *m_process.Lock() = HostProcess();
   m_main_thread = HostThread();
   if (m_image_file) {
     ::CloseHandle(m_image_file);
@@ -344,10 +343,10 @@ void DebuggerThread::DebugLoop() {
             HandleExitThreadEvent(dbe.u.ExitThread, dbe.dwThreadId);
         break;
       case EXIT_PROCESS_DEBUG_EVENT:
-        if (!::DuplicateHandle(::GetCurrentProcess(),
-                               m_process.GetNativeProcess().GetSystemHandle(),
-                               ::GetCurrentProcess(), &exited_process,
-                               SYNCHRONIZE, FALSE, 0))
+        if (!::DuplicateHandle(
+                ::GetCurrentProcess(),
+                GetProcess().GetNativeProcess().GetSystemHandle(),
+                ::GetCurrentProcess(), &exited_process, SYNCHRONIZE, FALSE, 0))
           exited_process = nullptr;
         should_debug = false;
         break;
@@ -493,7 +492,7 @@ DebuggerThread::HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info,
                                         DWORD thread_id) {
   Log *log = GetLog(WindowsLog::Event | WindowsLog::Thread);
   LLDB_LOG(log, "Thread {0} spawned in process {1}", thread_id,
-           m_process.GetProcessId());
+           GetProcess().GetProcessId());
   HostThread thread(info.hThread);
   thread.GetNativeThread().SetOwnsHandle(false);
   m_debug_delegate->OnCreateThread(thread);
@@ -558,9 +557,12 @@ DebuggerThread::HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info,
 
   // info.hProcess and info.hThread are closed automatically by Windows when
   // EXIT_PROCESS_DEBUG_EVENT is received.
-  m_process = HostProcess(info.hProcess);
-  static_cast<HostProcessWindows &>(m_process.GetNativeProcess())
-      .SetOwnsHandle(false);
+  {
+    auto process = m_process.Lock();
+    *process = HostProcess(info.hProcess);
+    static_cast<HostProcessWindows &>(process->GetNativeProcess())
+        .SetOwnsHandle(false);
+  }
   m_main_thread = HostThread(info.hThread);
   m_main_thread.GetNativeThread().SetOwnsHandle(false);
   m_image_file = info.hFile;
@@ -577,7 +579,7 @@ DebuggerThread::HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info,
                                       DWORD thread_id) {
   Log *log = GetLog(WindowsLog::Event | WindowsLog::Thread);
   LLDB_LOG(log, "Thread {0} exited with code {1} in process {2}", thread_id,
-           info.dwExitCode, m_process.GetProcessId());
+           info.dwExitCode, GetProcess().GetProcessId());
   m_debug_delegate->OnExitThread(thread_id, info.dwExitCode);
   return DBG_CONTINUE;
 }
@@ -586,7 +588,7 @@ DWORD
 DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info,
                                        DWORD thread_id) {
   Log *log = GetLog(WindowsLog::Event | WindowsLog::Thread);
-  LLDB_LOG(log, "process {0} exited with code {1}", m_process.GetProcessId(),
+  LLDB_LOG(log, "process {0} exited with code {1}", GetProcess().GetProcessId(),
            info.dwExitCode);
 
   m_debug_delegate->OnExitProcess(info.dwExitCode);
@@ -832,13 +834,13 @@ DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info,
     lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll);
 
     LLDB_LOG(log, "Inferior {0} - DLL '{1}' loaded at address {2:x}...",
-             m_process.GetProcessId(), path, info.lpBaseOfDll);
+             GetProcess().GetProcessId(), path, info.lpBaseOfDll);
 
     m_dll_event_pred.SetValue(false, eBroadcastNever);
     action = m_debug_delegate->OnLoadDll(module_spec, load_addr, thread_id);
   };
 
-  HANDLE process = m_process.GetNativeProcess().GetSystemHandle();
+  HANDLE process = GetProcess().GetNativeProcess().GetSystemHandle();
 
   // Prefer the path the loader used for the DLL, which keeps the subst drive,
   // junction or symbolic link it was reached through.
@@ -889,7 +891,7 @@ DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info,
     LLDB_LOG(log,
              "Inferior {0} - could not resolve path for LOAD_DLL_DEBUG_EVENT "
              "(hFile={1}, base={2:x}, last error={3})",
-             m_process.GetProcessId(), info.hFile, info.lpBaseOfDll,
+             GetProcess().GetProcessId(), info.hFile, info.lpBaseOfDll,
              ::GetLastError());
 
   // Windows does not automatically close info.hFile, so we need to do it.
@@ -908,7 +910,7 @@ DebuggerThread::HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info,
                                      DWORD thread_id) {
   Log *log = GetLog(WindowsLog::Event);
   LLDB_LOG(log, "process {0} unloading DLL at addr {1:x}.",
-           m_process.GetProcessId(), info.lpBaseOfDll);
+           GetProcess().GetProcessId(), info.lpBaseOfDll);
 
   m_dll_event_pred.SetValue(false, eBroadcastNever);
   DllEventAction action = m_debug_delegate->OnUnloadDll(
@@ -934,7 +936,7 @@ DWORD
 DebuggerThread::HandleRipEvent(const RIP_INFO &info, DWORD thread_id) {
   Log *log = GetLog(WindowsLog::Event);
   LLDB_LOG(log, "encountered error {0} (type={1}) in process {2} thread {3}",
-           info.dwError, info.dwType, m_process.GetProcessId(), thread_id);
+           info.dwError, info.dwType, GetProcess().GetProcessId(), thread_id);
 
   Status error(info.dwError, eErrorTypeWin32);
   m_debug_delegate->OnDebuggerError(error, info.dwType);

diff  --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h
index cc2d3f740b77e9..5f2dbb2ad34634 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h
@@ -17,6 +17,7 @@
 #include "lldb/Host/HostProcess.h"
 #include "lldb/Host/HostThread.h"
 #include "lldb/Host/windows/windows.h"
+#include "lldb/Utility/Locked.h"
 #include "lldb/Utility/Predicate.h"
 
 namespace lldb_private {
@@ -33,7 +34,7 @@ class DebuggerThread : public std::enable_shared_from_this<DebuggerThread> {
   Status DebugLaunch(const ProcessLaunchInfo &launch_info);
   Status DebugAttach(lldb::pid_t pid, const ProcessAttachInfo &attach_info);
 
-  HostProcess GetProcess() const { return m_process; }
+  HostProcess GetProcess() const { return *m_process.LockShared(); }
   HostThread GetMainThread() const { return m_main_thread; }
 
   /// The path the process was created with, as recorded in its process
@@ -79,7 +80,7 @@ class DebuggerThread : public std::enable_shared_from_this<DebuggerThread> {
 
   DebugDelegateSP m_debug_delegate;
 
-  HostProcess m_process;    // The process being debugged.
+  Guarded<HostProcess> m_process; // The process being debugged.
   HostThread m_main_thread; // The main thread of the inferior.
 
   // The image file of the process being debugged.


        


More information about the lldb-commits mailing list