Skip to content

Commit

Permalink
[lsan] Process non-suspended threads (llvm#112807)
Browse files Browse the repository at this point in the history
For such threads we have no registers, so no exact
stack range, and no guaranties that stack is mapped
at all.

To avoid crashes on unmapped memory,
 `MemCpyAccessible` copies intersting range into
 temporarily buffer, and we search for pointers there.
  • Loading branch information
vitalybuka authored Oct 18, 2024
1 parent c7496ce commit f4c6088
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 0 deletions.
41 changes: 41 additions & 0 deletions compiler-rt/lib/lsan/lsan_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,27 @@ struct DirectMemoryAccessor {
void Init(uptr begin, uptr end) {};
void *LoadPtr(uptr p) const { return *reinterpret_cast<void **>(p); }
};

struct CopyMemoryAccessor {
void Init(uptr begin, uptr end) {
this->begin = begin;
buffer.clear();
buffer.resize(end - begin);
MemCpyAccessible(buffer.data(), reinterpret_cast<void *>(begin),
buffer.size());
};

void *LoadPtr(uptr p) const {
uptr offset = p - begin;
CHECK_LE(offset + sizeof(void *), reinterpret_cast<uptr>(buffer.size()));
return *reinterpret_cast<void **>(offset +
reinterpret_cast<uptr>(buffer.data()));
}

private:
uptr begin;
InternalMmapVector<char> buffer;
};
} // namespace

// Scans the memory range, looking for byte patterns that point into allocator
Expand Down Expand Up @@ -535,6 +556,7 @@ static void ProcessThread(tid_t os_id, uptr sp,
static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
Frontier *frontier, tid_t caller_tid,
uptr caller_sp) {
InternalMmapVector<tid_t> done_threads;
InternalMmapVector<uptr> registers;
InternalMmapVector<Range> extra_ranges;
for (uptr i = 0; i < suspended_threads.ThreadCount(); i++) {
Expand All @@ -559,6 +581,25 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads,

DirectMemoryAccessor accessor;
ProcessThread(os_id, sp, registers, extra_ranges, frontier, accessor);
if (flags()->use_detached)
done_threads.push_back(os_id);
}

if (flags()->use_detached) {
CopyMemoryAccessor accessor;
InternalMmapVector<tid_t> known_threads;
GetRunningThreadsLocked(&known_threads);
Sort(done_threads.data(), done_threads.size());
for (tid_t os_id : known_threads) {
registers.clear();
extra_ranges.clear();

uptr i = InternalLowerBound(done_threads, os_id);
if (i >= done_threads.size() || done_threads[i] != os_id) {
uptr sp = (os_id == caller_tid) ? caller_sp : 0;
ProcessThread(os_id, sp, registers, extra_ranges, frontier, accessor);
}
}
}

// Add pointers reachable from ThreadContexts
Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/lsan/lsan_flags.inc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ LSAN_FLAG(bool, use_ld_allocations, true,
LSAN_FLAG(bool, use_unaligned, false, "Consider unaligned pointers valid.")
LSAN_FLAG(bool, use_poisoned, false,
"Consider pointers found in poisoned memory to be valid.")
LSAN_FLAG(bool, use_detached, false,
"Scan threads even if attaching to them failed.")
LSAN_FLAG(bool, log_pointers, false, "Debug logging")
LSAN_FLAG(bool, log_threads, false, "Debug logging")
LSAN_FLAG(int, tries, 1, "Debug option to repeat leak checking multiple times")
Expand Down

0 comments on commit f4c6088

Please sign in to comment.