Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 89 additions & 21 deletions compiler-rt/lib/lsan/lsan_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,23 +288,54 @@ static inline bool MaybeUserPointer(uptr p) {
# endif
}

namespace {
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
// chunks. Marks those chunks with |tag| and adds them to |frontier|.
// There are two usage modes for this function: finding reachable chunks
// (|tag| = kReachable) and finding indirectly leaked chunks
// (|tag| = kIndirectlyLeaked). In the second case, there's no flood fill,
// so |frontier| = 0.
void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier,
const char *region_type, ChunkTag tag) {
template <class Accessor>
void ScanForPointers(uptr begin, uptr end, Frontier *frontier,
const char *region_type, ChunkTag tag,
Accessor &accessor) {
CHECK(tag == kReachable || tag == kIndirectlyLeaked);
const uptr alignment = flags()->pointer_alignment();
LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, (void *)begin,
(void *)end);
accessor.Init(begin, end);
uptr pp = begin;
if (pp % alignment)
pp = pp + alignment - pp % alignment;
for (; pp + sizeof(void *) <= end; pp += alignment) {
void *p = *reinterpret_cast<void **>(pp);
void *p = accessor.LoadPtr(pp);
# if SANITIZER_APPLE
p = TransformPointer(p);
# endif
Expand Down Expand Up @@ -339,6 +370,12 @@ void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier,
}
}

void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier,
const char *region_type, ChunkTag tag) {
DirectMemoryAccessor accessor;
ScanForPointers(begin, end, frontier, region_type, tag, accessor);
}

// Scans a global range for pointers
void ScanGlobalRange(uptr begin, uptr end, Frontier *frontier) {
uptr allocator_begin = 0, allocator_end = 0;
Expand All @@ -356,14 +393,21 @@ void ScanGlobalRange(uptr begin, uptr end, Frontier *frontier) {
}
}

void ScanExtraStackRanges(const InternalMmapVector<Range> &ranges,
Frontier *frontier) {
template <class Accessor>
void ScanExtraStack(const InternalMmapVector<Range> &ranges, Frontier *frontier,
Accessor &accessor) {
for (uptr i = 0; i < ranges.size(); i++) {
ScanRangeForPointers(ranges[i].begin, ranges[i].end, frontier, "FAKE STACK",
kReachable);
ScanForPointers(ranges[i].begin, ranges[i].end, frontier, "FAKE STACK",
kReachable, accessor);
}
}

void ScanExtraStackRanges(const InternalMmapVector<Range> &ranges,
Frontier *frontier) {
DirectMemoryAccessor accessor;
ScanExtraStack(ranges, frontier, accessor);
}

# if SANITIZER_FUCHSIA

// Fuchsia handles all threads together with its own callback.
Expand Down Expand Up @@ -399,10 +443,11 @@ static void ProcessThreadRegistry(Frontier *frontier) {
}

// Scans thread data (stacks and TLS) for heap pointers.
template <class Accessor>
static void ProcessThread(tid_t os_id, uptr sp,
const InternalMmapVector<uptr> &registers,
InternalMmapVector<Range> &extra_ranges,
Frontier *frontier) {
Frontier *frontier, Accessor &accessor) {
// `extra_ranges` is outside of the function and the loop to reused mapped
// memory.
CHECK(extra_ranges.empty());
Expand All @@ -426,8 +471,8 @@ static void ProcessThread(tid_t os_id, uptr sp,
uptr registers_begin = reinterpret_cast<uptr>(registers.data());
uptr registers_end =
reinterpret_cast<uptr>(registers.data() + registers.size());
ScanRangeForPointers(registers_begin, registers_end, frontier, "REGISTERS",
kReachable);
ScanForPointers(registers_begin, registers_end, frontier, "REGISTERS",
kReachable, accessor);
}

if (flags()->use_stacks) {
Expand All @@ -451,9 +496,10 @@ static void ProcessThread(tid_t os_id, uptr sp,
// Shrink the stack range to ignore out-of-scope values.
stack_begin = sp;
}
ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK", kReachable);
ScanForPointers(stack_begin, stack_end, frontier, "STACK", kReachable,
accessor);
GetThreadExtraStackRangesLocked(os_id, &extra_ranges);
ScanExtraStackRanges(extra_ranges, frontier);
ScanExtraStack(extra_ranges, frontier, accessor);
}

if (flags()->use_tls) {
Expand All @@ -463,21 +509,23 @@ static void ProcessThread(tid_t os_id, uptr sp,
// otherwise, only scan the non-overlapping portions
if (cache_begin == cache_end || tls_end < cache_begin ||
tls_begin > cache_end) {
ScanRangeForPointers(tls_begin, tls_end, frontier, "TLS", kReachable);
ScanForPointers(tls_begin, tls_end, frontier, "TLS", kReachable,
accessor);
} else {
if (tls_begin < cache_begin)
ScanRangeForPointers(tls_begin, cache_begin, frontier, "TLS",
kReachable);
ScanForPointers(tls_begin, cache_begin, frontier, "TLS", kReachable,
accessor);
if (tls_end > cache_end)
ScanRangeForPointers(cache_end, tls_end, frontier, "TLS", kReachable);
ScanForPointers(cache_end, tls_end, frontier, "TLS", kReachable,
accessor);
}
}
# if SANITIZER_ANDROID
auto *cb = +[](void *dtls_begin, void *dtls_end, uptr /*dso_idd*/,
void *arg) -> void {
ScanRangeForPointers(
ScanForPointers(
reinterpret_cast<uptr>(dtls_begin), reinterpret_cast<uptr>(dtls_end),
reinterpret_cast<Frontier *>(arg), "DTLS", kReachable);
reinterpret_cast<Frontier *>(arg), "DTLS", kReachable, accessor);
};

// FIXME: There might be a race-condition here (and in Bionic) if the
Expand All @@ -492,8 +540,8 @@ static void ProcessThread(tid_t os_id, uptr sp,
if (dtls_beg < dtls_end) {
LOG_THREADS("DTLS %d at %p-%p.\n", id, (void *)dtls_beg,
(void *)dtls_end);
ScanRangeForPointers(dtls_beg, dtls_end, frontier, "DTLS",
kReachable);
ScanForPointers(dtls_beg, dtls_end, frontier, "DTLS", kReachable,
accessor);
}
});
} else {
Expand All @@ -508,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 @@ -530,7 +579,26 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
if (os_id == caller_tid)
sp = caller_sp;

ProcessThread(os_id, sp, registers, extra_ranges, frontier);
DirectMemoryAccessor accessor;
ProcessThread(os_id, sp, registers, extra_ranges, frontier, accessor);
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 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
Loading