[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