Skip to content

Commit e1bd337

Browse files
authored
[lldb] Fix ThreadList assignment race (#98293)
ThreadList uses the Process mutex to guard its state. This means its not possible to safely modify its process member, as the member is required to lock the mutex. Fortunately for us, we never actually need to change the process member (we always just juggle different kinds of thread lists belonging to the same process). This patch replaces the process member assignment (which is technically a race even when it assigns the same value) with an assertion. Since all this means that the class can never change its process member value (and it also must be non-null at all times), I've also changed the member type to a reference.
1 parent eb97739 commit e1bd337

File tree

4 files changed

+42
-41
lines changed

4 files changed

+42
-41
lines changed

lldb/include/lldb/Target/ThreadList.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ class ThreadList : public ThreadCollection {
2727
friend class Process;
2828

2929
public:
30-
ThreadList(Process *process);
30+
ThreadList(Process &process);
3131

3232
ThreadList(const ThreadList &rhs);
3333

3434
~ThreadList() override;
3535

36+
/// Precondition: both thread lists must be belong to the same process.
3637
const ThreadList &operator=(const ThreadList &rhs);
3738

3839
uint32_t GetSize(bool can_update = true);
@@ -135,6 +136,7 @@ class ThreadList : public ThreadCollection {
135136

136137
std::recursive_mutex &GetMutex() const override;
137138

139+
/// Precondition: both thread lists must be belong to the same process.
138140
void Update(ThreadList &rhs);
139141

140142
protected:
@@ -143,7 +145,7 @@ class ThreadList : public ThreadCollection {
143145
void NotifySelectedThreadChanged(lldb::tid_t tid);
144146

145147
// Classes that inherit from Process can see and modify these
146-
Process *m_process; ///< The process that manages this thread list.
148+
Process &m_process; ///< The process that manages this thread list.
147149
uint32_t
148150
m_stop_id; ///< The process stop ID that this thread list is valid for.
149151
lldb::tid_t

lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ lldb::ThreadSP OperatingSystemPython::CreateThread(lldb::tid_t tid,
372372

373373
std::vector<bool> core_used_map;
374374
if (thread_info_dict) {
375-
ThreadList core_threads(m_process);
375+
ThreadList core_threads(*m_process);
376376
ThreadList &thread_list = m_process->GetThreadList();
377377
bool did_create = false;
378378
ThreadSP thread_sp(

lldb/source/Target/Process.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -460,12 +460,10 @@ Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp,
460460
m_private_state_listener_sp(
461461
Listener::MakeListener("lldb.process.internal_state_listener")),
462462
m_mod_id(), m_process_unique_id(0), m_thread_index_id(0),
463-
m_thread_id_to_index_id_map(), m_exit_status(-1), m_exit_string(),
464-
m_exit_status_mutex(), m_thread_mutex(), m_thread_list_real(this),
465-
m_thread_list(this), m_thread_plans(*this), m_extended_thread_list(this),
466-
m_extended_thread_stop_id(0), m_queue_list(this), m_queue_list_stop_id(0),
467-
m_watchpoint_resource_list(), m_notifications(), m_image_tokens(),
468-
m_breakpoint_site_list(), m_dynamic_checkers_up(),
463+
m_thread_id_to_index_id_map(), m_exit_status(-1),
464+
m_thread_list_real(*this), m_thread_list(*this), m_thread_plans(*this),
465+
m_extended_thread_list(*this), m_extended_thread_stop_id(0),
466+
m_queue_list(this), m_queue_list_stop_id(0),
469467
m_unix_signals_sp(unix_signals_sp), m_abi_sp(), m_process_input_reader(),
470468
m_stdio_communication("process.stdio"), m_stdio_communication_mutex(),
471469
m_stdin_forward(false), m_stdout_data(), m_stderr_data(),
@@ -1183,8 +1181,8 @@ void Process::UpdateThreadListIfNeeded() {
11831181
// mutex between the call to UpdateThreadList(...) and the
11841182
// os->UpdateThreadList(...) so it doesn't change on us
11851183
ThreadList &old_thread_list = m_thread_list;
1186-
ThreadList real_thread_list(this);
1187-
ThreadList new_thread_list(this);
1184+
ThreadList real_thread_list(*this);
1185+
ThreadList new_thread_list(*this);
11881186
// Always update the thread list with the protocol specific thread list,
11891187
// but only update if "true" is returned
11901188
if (UpdateThreadList(m_thread_list_real, real_thread_list)) {

lldb/source/Target/ThreadList.cpp

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
using namespace lldb;
2424
using namespace lldb_private;
2525

26-
ThreadList::ThreadList(Process *process)
26+
ThreadList::ThreadList(Process &process)
2727
: ThreadCollection(), m_process(process), m_stop_id(0),
2828
m_selected_tid(LLDB_INVALID_THREAD_ID) {}
2929

@@ -36,14 +36,13 @@ ThreadList::ThreadList(const ThreadList &rhs)
3636

3737
const ThreadList &ThreadList::operator=(const ThreadList &rhs) {
3838
if (this != &rhs) {
39-
// Lock both mutexes to make sure neither side changes anyone on us while
40-
// the assignment occurs
41-
std::lock(GetMutex(), rhs.GetMutex());
42-
std::lock_guard<std::recursive_mutex> guard(GetMutex(), std::adopt_lock);
43-
std::lock_guard<std::recursive_mutex> rhs_guard(rhs.GetMutex(),
44-
std::adopt_lock);
45-
46-
m_process = rhs.m_process;
39+
// We only allow assignments between thread lists describing the same
40+
// process. Same process implies same mutex, which means it's enough to lock
41+
// just the current object.
42+
assert(&m_process == &rhs.m_process);
43+
assert(&GetMutex() == &rhs.GetMutex());
44+
std::lock_guard<std::recursive_mutex> guard(GetMutex());
45+
4746
m_stop_id = rhs.m_stop_id;
4847
m_threads = rhs.m_threads;
4948
m_selected_tid = rhs.m_selected_tid;
@@ -84,15 +83,15 @@ uint32_t ThreadList::GetSize(bool can_update) {
8483
std::lock_guard<std::recursive_mutex> guard(GetMutex());
8584

8685
if (can_update)
87-
m_process->UpdateThreadListIfNeeded();
86+
m_process.UpdateThreadListIfNeeded();
8887
return m_threads.size();
8988
}
9089

9190
ThreadSP ThreadList::GetThreadAtIndex(uint32_t idx, bool can_update) {
9291
std::lock_guard<std::recursive_mutex> guard(GetMutex());
9392

9493
if (can_update)
95-
m_process->UpdateThreadListIfNeeded();
94+
m_process.UpdateThreadListIfNeeded();
9695

9796
ThreadSP thread_sp;
9897
if (idx < m_threads.size())
@@ -104,7 +103,7 @@ ThreadSP ThreadList::FindThreadByID(lldb::tid_t tid, bool can_update) {
104103
std::lock_guard<std::recursive_mutex> guard(GetMutex());
105104

106105
if (can_update)
107-
m_process->UpdateThreadListIfNeeded();
106+
m_process.UpdateThreadListIfNeeded();
108107

109108
ThreadSP thread_sp;
110109
uint32_t idx = 0;
@@ -122,7 +121,7 @@ ThreadSP ThreadList::FindThreadByProtocolID(lldb::tid_t tid, bool can_update) {
122121
std::lock_guard<std::recursive_mutex> guard(GetMutex());
123122

124123
if (can_update)
125-
m_process->UpdateThreadListIfNeeded();
124+
m_process.UpdateThreadListIfNeeded();
126125

127126
ThreadSP thread_sp;
128127
uint32_t idx = 0;
@@ -140,7 +139,7 @@ ThreadSP ThreadList::RemoveThreadByID(lldb::tid_t tid, bool can_update) {
140139
std::lock_guard<std::recursive_mutex> guard(GetMutex());
141140

142141
if (can_update)
143-
m_process->UpdateThreadListIfNeeded();
142+
m_process.UpdateThreadListIfNeeded();
144143

145144
ThreadSP thread_sp;
146145
uint32_t idx = 0;
@@ -160,7 +159,7 @@ ThreadSP ThreadList::RemoveThreadByProtocolID(lldb::tid_t tid,
160159
std::lock_guard<std::recursive_mutex> guard(GetMutex());
161160

162161
if (can_update)
163-
m_process->UpdateThreadListIfNeeded();
162+
m_process.UpdateThreadListIfNeeded();
164163

165164
ThreadSP thread_sp;
166165
uint32_t idx = 0;
@@ -210,7 +209,7 @@ ThreadSP ThreadList::FindThreadByIndexID(uint32_t index_id, bool can_update) {
210209
std::lock_guard<std::recursive_mutex> guard(GetMutex());
211210

212211
if (can_update)
213-
m_process->UpdateThreadListIfNeeded();
212+
m_process.UpdateThreadListIfNeeded();
214213

215214
ThreadSP thread_sp;
216215
const uint32_t num_threads = m_threads.size();
@@ -241,7 +240,7 @@ bool ThreadList::ShouldStop(Event *event_ptr) {
241240
// Scope for locker
242241
std::lock_guard<std::recursive_mutex> guard(GetMutex());
243242

244-
m_process->UpdateThreadListIfNeeded();
243+
m_process.UpdateThreadListIfNeeded();
245244
for (lldb::ThreadSP thread_sp : m_threads) {
246245
// This is an optimization... If we didn't let a thread run in between
247246
// the previous stop and this one, we shouldn't have to consult it for
@@ -377,7 +376,7 @@ Vote ThreadList::ShouldReportStop(Event *event_ptr) {
377376
std::lock_guard<std::recursive_mutex> guard(GetMutex());
378377

379378
Vote result = eVoteNoOpinion;
380-
m_process->UpdateThreadListIfNeeded();
379+
m_process.UpdateThreadListIfNeeded();
381380
collection::iterator pos, end = m_threads.end();
382381

383382
Log *log = GetLog(LLDBLog::Step);
@@ -425,7 +424,7 @@ Vote ThreadList::ShouldReportStop(Event *event_ptr) {
425424
void ThreadList::SetShouldReportStop(Vote vote) {
426425
std::lock_guard<std::recursive_mutex> guard(GetMutex());
427426

428-
m_process->UpdateThreadListIfNeeded();
427+
m_process.UpdateThreadListIfNeeded();
429428
collection::iterator pos, end = m_threads.end();
430429
for (pos = m_threads.begin(); pos != end; ++pos) {
431430
ThreadSP thread_sp(*pos);
@@ -438,7 +437,7 @@ Vote ThreadList::ShouldReportRun(Event *event_ptr) {
438437
std::lock_guard<std::recursive_mutex> guard(GetMutex());
439438

440439
Vote result = eVoteNoOpinion;
441-
m_process->UpdateThreadListIfNeeded();
440+
m_process.UpdateThreadListIfNeeded();
442441
collection::iterator pos, end = m_threads.end();
443442

444443
// Run through the threads and ask whether we should report this event. The
@@ -486,7 +485,7 @@ void ThreadList::Destroy() {
486485
void ThreadList::RefreshStateAfterStop() {
487486
std::lock_guard<std::recursive_mutex> guard(GetMutex());
488487

489-
m_process->UpdateThreadListIfNeeded();
488+
m_process.UpdateThreadListIfNeeded();
490489

491490
Log *log = GetLog(LLDBLog::Step);
492491
if (log && log->GetVerbose())
@@ -515,7 +514,7 @@ bool ThreadList::WillResume() {
515514
// they are.
516515

517516
std::lock_guard<std::recursive_mutex> guard(GetMutex());
518-
m_process->UpdateThreadListIfNeeded();
517+
m_process.UpdateThreadListIfNeeded();
519518

520519
collection::iterator pos, end = m_threads.end();
521520

@@ -546,13 +545,13 @@ bool ThreadList::WillResume() {
546545
if (log && log->GetVerbose())
547546
LLDB_LOGF(log, "Turning on notification of new threads while single "
548547
"stepping a thread.");
549-
m_process->StartNoticingNewThreads();
548+
m_process.StartNoticingNewThreads();
550549
} else {
551550
Log *log = GetLog(LLDBLog::Step);
552551
if (log && log->GetVerbose())
553552
LLDB_LOGF(log, "Turning off notification of new threads while single "
554553
"stepping a thread.");
555-
m_process->StopNoticingNewThreads();
554+
m_process.StopNoticingNewThreads();
556555
}
557556

558557
// Give all the threads that are likely to run a last chance to set up their
@@ -575,7 +574,7 @@ bool ThreadList::WillResume() {
575574

576575
ThreadList run_me_only_list(m_process);
577576

578-
run_me_only_list.SetStopID(m_process->GetStopID());
577+
run_me_only_list.SetStopID(m_process.GetStopID());
579578

580579
// One or more threads might want to "Stop Others". We want to handle all
581580
// those requests first. And if there is a thread that wanted to "resume
@@ -736,11 +735,13 @@ void ThreadList::NotifySelectedThreadChanged(lldb::tid_t tid) {
736735

737736
void ThreadList::Update(ThreadList &rhs) {
738737
if (this != &rhs) {
739-
// Lock both mutexes to make sure neither side changes anyone on us while
740-
// the assignment occurs
741-
std::scoped_lock<std::recursive_mutex, std::recursive_mutex> guard(GetMutex(), rhs.GetMutex());
738+
// We only allow assignments between thread lists describing the same
739+
// process. Same process implies same mutex, which means it's enough to lock
740+
// just the current object.
741+
assert(&m_process == &rhs.m_process);
742+
assert(&GetMutex() == &rhs.GetMutex());
743+
std::lock_guard<std::recursive_mutex> guard(GetMutex());
742744

743-
m_process = rhs.m_process;
744745
m_stop_id = rhs.m_stop_id;
745746
m_threads.swap(rhs.m_threads);
746747
m_selected_tid = rhs.m_selected_tid;
@@ -784,7 +785,7 @@ void ThreadList::Flush() {
784785
}
785786

786787
std::recursive_mutex &ThreadList::GetMutex() const {
787-
return m_process->m_thread_mutex;
788+
return m_process.m_thread_mutex;
788789
}
789790

790791
ThreadList::ExpressionExecutionThreadPusher::ExpressionExecutionThreadPusher(

0 commit comments

Comments
 (0)