diff --git a/lldb/include/lldb/Target/ThreadPlan.h b/lldb/include/lldb/Target/ThreadPlan.h --- a/lldb/include/lldb/Target/ThreadPlan.h +++ b/lldb/include/lldb/Target/ThreadPlan.h @@ -600,9 +600,8 @@ // For ThreadPlan only static lldb::user_id_t GetNextID(); - Thread *m_thread; // Stores a cached value of the thread, which is set to - // nullptr when the thread resumes. Don't use this anywhere - // but ThreadPlan::GetThread(). + lldb::ThreadSP m_thread_sp; // Stores a cached value of the thread. Don't use + // use this anywhere but ThreadPlan::GetThread(). ThreadPlanKind m_kind; std::string m_name; std::recursive_mutex m_plan_complete_mutex; diff --git a/lldb/source/Target/ThreadPlan.cpp b/lldb/source/Target/ThreadPlan.cpp --- a/lldb/source/Target/ThreadPlan.cpp +++ b/lldb/source/Target/ThreadPlan.cpp @@ -24,10 +24,10 @@ : m_process(*thread.GetProcess().get()), m_tid(thread.GetID()), m_stop_vote(stop_vote), m_run_vote(run_vote), m_takes_iteration_count(false), m_could_not_resolve_hw_bp(false), - m_thread(&thread), m_kind(kind), m_name(name), m_plan_complete_mutex(), - m_cached_plan_explains_stop(eLazyBoolCalculate), m_plan_complete(false), - m_plan_private(false), m_okay_to_discard(true), m_is_master_plan(false), - m_plan_succeeded(true) { + m_thread_sp(thread.shared_from_this()), m_kind(kind), m_name(name), + m_plan_complete_mutex(), m_cached_plan_explains_stop(eLazyBoolCalculate), + m_plan_complete(false), m_plan_private(false), m_okay_to_discard(true), + m_is_master_plan(false), m_plan_succeeded(true) { SetID(GetNextID()); } @@ -39,12 +39,11 @@ const Target &ThreadPlan::GetTarget() const { return m_process.GetTarget(); } Thread &ThreadPlan::GetThread() { - if (m_thread) - return *m_thread; + if (m_thread_sp && m_thread_sp->IsValid()) + return *m_thread_sp; - ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(m_tid); - m_thread = thread_sp.get(); - return *m_thread; + m_thread_sp = m_process.GetThreadList().FindThreadByID(m_tid); + return *m_thread_sp; } bool ThreadPlan::PlanExplainsStop(Event *event_ptr) { @@ -133,11 +132,7 @@ StateAsCString(resume_state), StopOthers()); } } - bool success = DoWillResume(resume_state, current_plan); - m_thread = nullptr; // We don't cache the thread pointer over resumes. This - // Thread might go away, and another Thread represent - // the same underlying object on a later stop. - return success; + return DoWillResume(resume_state, current_plan); } lldb::user_id_t ThreadPlan::GetNextID() { diff --git a/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/TestOSPluginStepping.py b/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/TestOSPluginStepping.py --- a/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/TestOSPluginStepping.py +++ b/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/TestOSPluginStepping.py @@ -51,6 +51,13 @@ (target, self.process, thread, thread_bkpt) = lldbutil.run_to_source_breakpoint( self, "first stop in thread - do a step out", self.main_file) + # Disabling the thread breakpoint here ensures that we don't have an + # unreported stop (to step over that breakpoint), which ensures that + # ThreadPlan::ShouldReportRun is called in between ThreadPlan::WillResume + # and when the thread actually disappears. This previously triggered + # a use-after-free. + thread_bkpt.SetEnabled(False) + main_bkpt = target.BreakpointCreateBySourceRegex('Stop here and do not make a memory thread for thread_1', self.main_file) self.assertEqual(main_bkpt.GetNumLocations(), 1, "Main breakpoint has one location")