https://github.com/Teemperor updated 
https://github.com/llvm/llvm-project/pull/195910

>From 19af6669a4b1fec950811ecbdce1e630da1b7013 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

Reply via email to