https://github.com/Teemperor updated https://github.com/llvm/llvm-project/pull/195910
>From 633b4b6b2b6ad2661c6c304ef638a0d49520f49c Mon Sep 17 00:00:00 2001 From: Raphael Isemann <[email protected]> Date: Tue, 5 May 2026 19:14:38 +0100 Subject: [PATCH] [lldb] Fix nullptr deref in ThreadPlanCallFunction::DoTakedown When running an expression that calls `exit`, we can end up in a case where we call `ThreadPlan::GetThread` without having a current thread. The backtrace for this looks like this: ``` ThreadPlan::GetThread ThreadPlanCallFunction::DoTakedown ThreadPlanCallFunction::~ThreadPlanCallFunction ThreadPlanCallUserExpression::~ThreadPlanCallUserExpression ``` The problem is that `ThreadPlan::GetThread` returns a `Thread &` and cannot communicate that there is no thread to be returned. In practice, what we end up returning is a nullptr dereferenced to a nullptr reference, which is UB. This then causes the associated test TestExitDuringExpression.py to fail with UBSan. This patch just avoids calling `ThreadPlan::GetThread` in this one specific edge case to avoid the issue. It also adds an assert to make this an error we can see without having to run UBSan. I didn't change the `ThreadPlan::GetThread` API as I can only see this one specific edge case where we can encounter this situation. If we made `ThreadPlan::GetThread` return an optional-like value, then we had to check the result in about 50 different places, and all of that code would be effectively unreachable then. --- lldb/source/Target/ThreadPlan.cpp | 1 + lldb/source/Target/ThreadPlanCallFunction.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lldb/source/Target/ThreadPlan.cpp b/lldb/source/Target/ThreadPlan.cpp index 26ffbf84be6f0..df42da9df84e0 100644 --- a/lldb/source/Target/ThreadPlan.cpp +++ b/lldb/source/Target/ThreadPlan.cpp @@ -45,6 +45,7 @@ Thread &ThreadPlan::GetThread() { ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(m_tid); m_thread = thread_sp.get(); + assert(m_thread); return *m_thread; } diff --git a/lldb/source/Target/ThreadPlanCallFunction.cpp b/lldb/source/Target/ThreadPlanCallFunction.cpp index 427693f02722f..d0b9a2fca446d 100644 --- a/lldb/source/Target/ThreadPlanCallFunction.cpp +++ b/lldb/source/Target/ThreadPlanCallFunction.cpp @@ -174,14 +174,14 @@ void ThreadPlanCallFunction::ReportRegisterState(const char *message) { void ThreadPlanCallFunction::DoTakedown(bool success) { Log *log = GetLog(LLDBLog::Step); - Thread &thread = GetThread(); if (!m_valid) { // If ConstructorSetup was succesfull but PrepareTrivialCall was not, // we will have a saved register state and potentially modified registers. // Restore those. if (m_stored_thread_state.register_backup_sp) - if (!thread.RestoreRegisterStateFromCheckpoint(m_stored_thread_state)) + if (!GetThread().RestoreRegisterStateFromCheckpoint( + m_stored_thread_state)) LLDB_LOGF( log, "ThreadPlanCallFunction(%p): Failed to restore register state from " @@ -205,6 +205,7 @@ void ThreadPlanCallFunction::DoTakedown(bool success) { "0x%4.4" PRIx64 ", m_valid: %d complete: %d.\n", static_cast<void *>(this), m_tid, m_valid, IsPlanComplete()); m_takedown_done = true; + Thread &thread = GetThread(); m_stop_address = thread.GetStackFrameAtIndex(0)->GetRegisterContext()->GetPC(); m_real_stop_info_sp = GetPrivateStopInfo(); _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
