mirror of
https://github.com/opnsense/src.git
synced 2026-05-28 04:12:45 -04:00
Vendor import of compiler-rt trunk r300890:
https://llvm.org/svn/llvm-project/compiler-rt/trunk@300890
This commit is contained in:
parent
ab0bf875a5
commit
f351c8a560
90 changed files with 941 additions and 1198 deletions
|
|
@ -35,35 +35,6 @@ extern "C" {
|
|||
// Get the number of unique covered blocks (or edges).
|
||||
// This can be useful for coverage-directed in-process fuzzers.
|
||||
uintptr_t __sanitizer_get_total_unique_coverage();
|
||||
// Get the number of unique indirect caller-callee pairs.
|
||||
uintptr_t __sanitizer_get_total_unique_caller_callee_pairs();
|
||||
|
||||
// Reset the basic-block (edge) coverage to the initial state.
|
||||
// Useful for in-process fuzzing to start collecting coverage from scratch.
|
||||
// Experimental, will likely not work for multi-threaded process.
|
||||
void __sanitizer_reset_coverage();
|
||||
// Set *data to the array of covered PCs and return the size of that array.
|
||||
// Some of the entries in *data will be zero.
|
||||
uintptr_t __sanitizer_get_coverage_guards(uintptr_t **data);
|
||||
|
||||
// The coverage instrumentation may optionally provide imprecise counters.
|
||||
// Rather than exposing the counter values to the user we instead map
|
||||
// the counters to a bitset.
|
||||
// Every counter is associated with 8 bits in the bitset.
|
||||
// We define 8 value ranges: 1, 2, 3, 4-7, 8-15, 16-31, 32-127, 128+
|
||||
// The i-th bit is set to 1 if the counter value is in the i-th range.
|
||||
// This counter-based coverage implementation is *not* thread-safe.
|
||||
|
||||
// Returns the number of registered coverage counters.
|
||||
uintptr_t __sanitizer_get_number_of_counters();
|
||||
// Updates the counter 'bitset', clears the counters and returns the number of
|
||||
// new bits in 'bitset'.
|
||||
// If 'bitset' is nullptr, only clears the counters.
|
||||
// Otherwise 'bitset' should be at least
|
||||
// __sanitizer_get_number_of_counters bytes long and 8-aligned.
|
||||
uintptr_t
|
||||
__sanitizer_update_counter_bitset_and_clear_counters(uint8_t *bitset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ void AsanThread::Init() {
|
|||
}
|
||||
|
||||
thread_return_t AsanThread::ThreadStart(
|
||||
uptr os_id, atomic_uintptr_t *signal_thread_is_registered) {
|
||||
tid_t os_id, atomic_uintptr_t *signal_thread_is_registered) {
|
||||
Init();
|
||||
asanThreadRegistry().StartThread(tid(), os_id, /*workerthread*/ false,
|
||||
nullptr);
|
||||
|
|
@ -395,7 +395,7 @@ void EnsureMainThreadIDIsCorrect() {
|
|||
context->os_id = GetTid();
|
||||
}
|
||||
|
||||
__asan::AsanThread *GetAsanThreadByOsIDLocked(uptr os_id) {
|
||||
__asan::AsanThread *GetAsanThreadByOsIDLocked(tid_t os_id) {
|
||||
__asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>(
|
||||
__asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id));
|
||||
if (!context) return nullptr;
|
||||
|
|
@ -405,7 +405,7 @@ __asan::AsanThread *GetAsanThreadByOsIDLocked(uptr os_id) {
|
|||
|
||||
// --- Implementation of LSan-specific functions --- {{{1
|
||||
namespace __lsan {
|
||||
bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
|
||||
bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end,
|
||||
uptr *tls_begin, uptr *tls_end, uptr *cache_begin,
|
||||
uptr *cache_end, DTLS **dtls) {
|
||||
__asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
|
||||
|
|
@ -421,7 +421,7 @@ bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
|
|||
return true;
|
||||
}
|
||||
|
||||
void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback,
|
||||
void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback,
|
||||
void *arg) {
|
||||
__asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
|
||||
if (t && t->has_fake_stack())
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class AsanThread {
|
|||
void Destroy();
|
||||
|
||||
void Init(); // Should be called from the thread itself.
|
||||
thread_return_t ThreadStart(uptr os_id,
|
||||
thread_return_t ThreadStart(tid_t os_id,
|
||||
atomic_uintptr_t *signal_thread_is_registered);
|
||||
|
||||
uptr stack_top();
|
||||
|
|
|
|||
|
|
@ -13,15 +13,23 @@
|
|||
#include "asan_test_utils.h"
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
|
||||
// Default ASAN_OPTIONS for the unit tests. Let's turn symbolication off to
|
||||
// speed up testing (unit tests don't use it anyway).
|
||||
// Default ASAN_OPTIONS for the unit tests.
|
||||
extern "C" const char* __asan_default_options() {
|
||||
#if SANITIZER_MAC
|
||||
// On Darwin, we default to `abort_on_error=1`, which would make tests run
|
||||
// much slower. Let's override this and run lit tests with 'abort_on_error=0'.
|
||||
// Also, make sure we do not overwhelm the syslog while testing.
|
||||
// much slower. Let's override this and run lit tests with 'abort_on_error=0'
|
||||
// and make sure we do not overwhelm the syslog while testing. Also, let's
|
||||
// turn symbolization off to speed up testing, especially when not running
|
||||
// with llvm-symbolizer but with atos.
|
||||
return "symbolize=false:abort_on_error=0:log_to_syslog=0";
|
||||
#elif SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT
|
||||
// On PowerPC and ARM Thumb, a couple tests involving pthread_exit fail due to
|
||||
// leaks detected by LSan. Symbolized leak report is required to apply a
|
||||
// suppression for this known problem.
|
||||
return "";
|
||||
#else
|
||||
// Let's turn symbolization off to speed up testing (more than 3 times speedup
|
||||
// observed).
|
||||
return "symbolize=false";
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -285,22 +285,8 @@ fun:__sanitizer_cov_module_init=uninstrumented
|
|||
fun:__sanitizer_cov_module_init=discard
|
||||
fun:__sanitizer_cov_with_check=uninstrumented
|
||||
fun:__sanitizer_cov_with_check=discard
|
||||
fun:__sanitizer_cov_indir_call16=uninstrumented
|
||||
fun:__sanitizer_cov_indir_call16=discard
|
||||
fun:__sanitizer_cov_indir_call16=uninstrumented
|
||||
fun:__sanitizer_cov_indir_call16=discard
|
||||
fun:__sanitizer_reset_coverage=uninstrumented
|
||||
fun:__sanitizer_reset_coverage=discard
|
||||
fun:__sanitizer_set_death_callback=uninstrumented
|
||||
fun:__sanitizer_set_death_callback=discard
|
||||
fun:__sanitizer_get_coverage_guards=uninstrumented
|
||||
fun:__sanitizer_get_coverage_guards=discard
|
||||
fun:__sanitizer_get_number_of_counters=uninstrumented
|
||||
fun:__sanitizer_get_number_of_counters=discard
|
||||
fun:__sanitizer_update_counter_bitset_and_clear_counters=uninstrumented
|
||||
fun:__sanitizer_update_counter_bitset_and_clear_counters=discard
|
||||
fun:__sanitizer_get_total_unique_coverage=uninstrumented
|
||||
fun:__sanitizer_get_total_unique_coverage=discard
|
||||
fun:__sanitizer_get_total_unique_coverage=uninstrumented
|
||||
fun:__sanitizer_get_total_unique_coverage=discard
|
||||
fun:__sanitizer_update_counter_bitset_and_clear_counters=uninstrumented
|
||||
|
|
|
|||
|
|
@ -68,6 +68,14 @@ ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
|
|||
static SuppressionContext *suppression_ctx = nullptr;
|
||||
static const char kSuppressionLeak[] = "leak";
|
||||
static const char *kSuppressionTypes[] = { kSuppressionLeak };
|
||||
static const char kStdSuppressions[] =
|
||||
#if SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT
|
||||
// The actual string allocation happens here (for more details refer to the
|
||||
// SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT definition).
|
||||
"leak:*_dl_map_object_deps*";
|
||||
#else
|
||||
"";
|
||||
#endif // SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT
|
||||
|
||||
void InitializeSuppressions() {
|
||||
CHECK_EQ(nullptr, suppression_ctx);
|
||||
|
|
@ -76,6 +84,7 @@ void InitializeSuppressions() {
|
|||
suppression_ctx->ParseFromFile(flags()->suppressions);
|
||||
if (&__lsan_default_suppressions)
|
||||
suppression_ctx->Parse(__lsan_default_suppressions());
|
||||
suppression_ctx->Parse(kStdSuppressions);
|
||||
}
|
||||
|
||||
static SuppressionContext *GetSuppressionContext() {
|
||||
|
|
@ -83,12 +92,9 @@ static SuppressionContext *GetSuppressionContext() {
|
|||
return suppression_ctx;
|
||||
}
|
||||
|
||||
struct RootRegion {
|
||||
const void *begin;
|
||||
uptr size;
|
||||
};
|
||||
static InternalMmapVector<RootRegion> *root_regions;
|
||||
|
||||
InternalMmapVector<RootRegion> *root_regions;
|
||||
InternalMmapVector<RootRegion> const *GetRootRegions() { return root_regions; }
|
||||
|
||||
void InitializeRootRegions() {
|
||||
CHECK(!root_regions);
|
||||
|
|
@ -200,11 +206,11 @@ void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg) {
|
|||
// Scans thread data (stacks and TLS) for heap pointers.
|
||||
static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
|
||||
Frontier *frontier) {
|
||||
InternalScopedBuffer<uptr> registers(SuspendedThreadsList::RegisterCount());
|
||||
InternalScopedBuffer<uptr> registers(suspended_threads.RegisterCount());
|
||||
uptr registers_begin = reinterpret_cast<uptr>(registers.data());
|
||||
uptr registers_end = registers_begin + registers.size();
|
||||
for (uptr i = 0; i < suspended_threads.thread_count(); i++) {
|
||||
uptr os_id = static_cast<uptr>(suspended_threads.GetThreadID(i));
|
||||
for (uptr i = 0; i < suspended_threads.ThreadCount(); i++) {
|
||||
tid_t os_id = static_cast<tid_t>(suspended_threads.GetThreadID(i));
|
||||
LOG_THREADS("Processing thread %d.\n", os_id);
|
||||
uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end;
|
||||
DTLS *dtls;
|
||||
|
|
@ -291,23 +297,29 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
|
|||
}
|
||||
}
|
||||
|
||||
static void ProcessRootRegion(Frontier *frontier, uptr root_begin,
|
||||
uptr root_end) {
|
||||
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
|
||||
void ScanRootRegion(Frontier *frontier, const RootRegion &root_region,
|
||||
uptr region_begin, uptr region_end, uptr prot) {
|
||||
uptr intersection_begin = Max(root_region.begin, region_begin);
|
||||
uptr intersection_end = Min(region_end, root_region.begin + root_region.size);
|
||||
if (intersection_begin >= intersection_end) return;
|
||||
bool is_readable = prot & MemoryMappingLayout::kProtectionRead;
|
||||
LOG_POINTERS("Root region %p-%p intersects with mapped region %p-%p (%s)\n",
|
||||
root_region.begin, root_region.begin + root_region.size,
|
||||
region_begin, region_end,
|
||||
is_readable ? "readable" : "unreadable");
|
||||
if (is_readable)
|
||||
ScanRangeForPointers(intersection_begin, intersection_end, frontier, "ROOT",
|
||||
kReachable);
|
||||
}
|
||||
|
||||
static void ProcessRootRegion(Frontier *frontier,
|
||||
const RootRegion &root_region) {
|
||||
MemoryMappingLayout proc_maps(/*cache_enabled*/ true);
|
||||
uptr begin, end, prot;
|
||||
while (proc_maps.Next(&begin, &end,
|
||||
/*offset*/ nullptr, /*filename*/ nullptr,
|
||||
/*filename_size*/ 0, &prot)) {
|
||||
uptr intersection_begin = Max(root_begin, begin);
|
||||
uptr intersection_end = Min(end, root_end);
|
||||
if (intersection_begin >= intersection_end) continue;
|
||||
bool is_readable = prot & MemoryMappingLayout::kProtectionRead;
|
||||
LOG_POINTERS("Root region %p-%p intersects with mapped region %p-%p (%s)\n",
|
||||
root_begin, root_end, begin, end,
|
||||
is_readable ? "readable" : "unreadable");
|
||||
if (is_readable)
|
||||
ScanRangeForPointers(intersection_begin, intersection_end, frontier,
|
||||
"ROOT", kReachable);
|
||||
ScanRootRegion(frontier, root_region, begin, end, prot);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -316,9 +328,7 @@ static void ProcessRootRegions(Frontier *frontier) {
|
|||
if (!flags()->use_root_regions) return;
|
||||
CHECK(root_regions);
|
||||
for (uptr i = 0; i < root_regions->size(); i++) {
|
||||
RootRegion region = (*root_regions)[i];
|
||||
uptr begin_addr = reinterpret_cast<uptr>(region.begin);
|
||||
ProcessRootRegion(frontier, begin_addr, begin_addr + region.size);
|
||||
ProcessRootRegion(frontier, (*root_regions)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -356,6 +366,72 @@ static void CollectIgnoredCb(uptr chunk, void *arg) {
|
|||
}
|
||||
}
|
||||
|
||||
static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) {
|
||||
CHECK(stack_id);
|
||||
StackTrace stack = map->Get(stack_id);
|
||||
// The top frame is our malloc/calloc/etc. The next frame is the caller.
|
||||
if (stack.size >= 2)
|
||||
return stack.trace[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct InvalidPCParam {
|
||||
Frontier *frontier;
|
||||
StackDepotReverseMap *stack_depot_reverse_map;
|
||||
bool skip_linker_allocations;
|
||||
};
|
||||
|
||||
// ForEachChunk callback. If the caller pc is invalid or is within the linker,
|
||||
// mark as reachable. Called by ProcessPlatformSpecificAllocations.
|
||||
static void MarkInvalidPCCb(uptr chunk, void *arg) {
|
||||
CHECK(arg);
|
||||
InvalidPCParam *param = reinterpret_cast<InvalidPCParam *>(arg);
|
||||
chunk = GetUserBegin(chunk);
|
||||
LsanMetadata m(chunk);
|
||||
if (m.allocated() && m.tag() != kReachable && m.tag() != kIgnored) {
|
||||
u32 stack_id = m.stack_trace_id();
|
||||
uptr caller_pc = 0;
|
||||
if (stack_id > 0)
|
||||
caller_pc = GetCallerPC(stack_id, param->stack_depot_reverse_map);
|
||||
// If caller_pc is unknown, this chunk may be allocated in a coroutine. Mark
|
||||
// it as reachable, as we can't properly report its allocation stack anyway.
|
||||
if (caller_pc == 0 || (param->skip_linker_allocations &&
|
||||
GetLinker()->containsAddress(caller_pc))) {
|
||||
m.set_tag(kReachable);
|
||||
param->frontier->push_back(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// On Linux, handles dynamically allocated TLS blocks by treating all chunks
|
||||
// allocated from ld-linux.so as reachable.
|
||||
// Dynamic TLS blocks contain the TLS variables of dynamically loaded modules.
|
||||
// They are allocated with a __libc_memalign() call in allocate_and_init()
|
||||
// (elf/dl-tls.c). Glibc won't tell us the address ranges occupied by those
|
||||
// blocks, but we can make sure they come from our own allocator by intercepting
|
||||
// __libc_memalign(). On top of that, there is no easy way to reach them. Their
|
||||
// addresses are stored in a dynamically allocated array (the DTV) which is
|
||||
// referenced from the static TLS. Unfortunately, we can't just rely on the DTV
|
||||
// being reachable from the static TLS, and the dynamic TLS being reachable from
|
||||
// the DTV. This is because the initial DTV is allocated before our interception
|
||||
// mechanism kicks in, and thus we don't recognize it as allocated memory. We
|
||||
// can't special-case it either, since we don't know its size.
|
||||
// Our solution is to include in the root set all allocations made from
|
||||
// ld-linux.so (which is where allocate_and_init() is implemented). This is
|
||||
// guaranteed to include all dynamic TLS blocks (and possibly other allocations
|
||||
// which we don't care about).
|
||||
// On all other platforms, this simply checks to ensure that the caller pc is
|
||||
// valid before reporting chunks as leaked.
|
||||
void ProcessPC(Frontier *frontier) {
|
||||
StackDepotReverseMap stack_depot_reverse_map;
|
||||
InvalidPCParam arg;
|
||||
arg.frontier = frontier;
|
||||
arg.stack_depot_reverse_map = &stack_depot_reverse_map;
|
||||
arg.skip_linker_allocations =
|
||||
flags()->use_tls && flags()->use_ld_allocations && GetLinker() != nullptr;
|
||||
ForEachChunk(MarkInvalidPCCb, &arg);
|
||||
}
|
||||
|
||||
// Sets the appropriate tag on each chunk.
|
||||
static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) {
|
||||
// Holds the flood fill frontier.
|
||||
|
|
@ -367,11 +443,13 @@ static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) {
|
|||
ProcessRootRegions(&frontier);
|
||||
FloodFillTag(&frontier, kReachable);
|
||||
|
||||
CHECK_EQ(0, frontier.size());
|
||||
ProcessPC(&frontier);
|
||||
|
||||
// The check here is relatively expensive, so we do this in a separate flood
|
||||
// fill. That way we can skip the check for chunks that are reachable
|
||||
// otherwise.
|
||||
LOG_POINTERS("Processing platform-specific allocations.\n");
|
||||
CHECK_EQ(0, frontier.size());
|
||||
ProcessPlatformSpecificAllocations(&frontier);
|
||||
FloodFillTag(&frontier, kReachable);
|
||||
|
||||
|
|
@ -707,7 +785,7 @@ void __lsan_register_root_region(const void *begin, uptr size) {
|
|||
#if CAN_SANITIZE_LEAKS
|
||||
BlockingMutexLock l(&global_mutex);
|
||||
CHECK(root_regions);
|
||||
RootRegion region = {begin, size};
|
||||
RootRegion region = {reinterpret_cast<uptr>(begin), size};
|
||||
root_regions->push_back(region);
|
||||
VReport(1, "Registered root region at %p of size %llu\n", begin, size);
|
||||
#endif // CAN_SANITIZE_LEAKS
|
||||
|
|
@ -721,7 +799,7 @@ void __lsan_unregister_root_region(const void *begin, uptr size) {
|
|||
bool removed = false;
|
||||
for (uptr i = 0; i < root_regions->size(); i++) {
|
||||
RootRegion region = (*root_regions)[i];
|
||||
if (region.begin == begin && region.size == size) {
|
||||
if (region.begin == reinterpret_cast<uptr>(begin) && region.size == size) {
|
||||
removed = true;
|
||||
uptr last_index = root_regions->size() - 1;
|
||||
(*root_regions)[i] = (*root_regions)[last_index];
|
||||
|
|
|
|||
|
|
@ -118,6 +118,15 @@ typedef InternalMmapVector<uptr> Frontier;
|
|||
void InitializePlatformSpecificModules();
|
||||
void ProcessGlobalRegions(Frontier *frontier);
|
||||
void ProcessPlatformSpecificAllocations(Frontier *frontier);
|
||||
|
||||
struct RootRegion {
|
||||
uptr begin;
|
||||
uptr size;
|
||||
};
|
||||
|
||||
InternalMmapVector<RootRegion> const *GetRootRegions();
|
||||
void ScanRootRegion(Frontier *frontier, RootRegion const ®ion,
|
||||
uptr region_begin, uptr region_end, uptr prot);
|
||||
// Run stoptheworld while holding any platform-specific locks.
|
||||
void DoStopTheWorld(StopTheWorldCallback callback, void* argument);
|
||||
|
||||
|
|
@ -193,10 +202,10 @@ bool WordIsPoisoned(uptr addr);
|
|||
// Wrappers for ThreadRegistry access.
|
||||
void LockThreadRegistry();
|
||||
void UnlockThreadRegistry();
|
||||
bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
|
||||
bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end,
|
||||
uptr *tls_begin, uptr *tls_end, uptr *cache_begin,
|
||||
uptr *cache_end, DTLS **dtls);
|
||||
void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback,
|
||||
void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback,
|
||||
void *arg);
|
||||
// If called from the main thread, updates the main thread's TID in the thread
|
||||
// registry. We need this to handle processes that fork() without a subsequent
|
||||
|
|
@ -212,6 +221,10 @@ uptr PointsIntoChunk(void *p);
|
|||
uptr GetUserBegin(uptr chunk);
|
||||
// Helper for __lsan_ignore_object().
|
||||
IgnoreObjectResult IgnoreObjectLocked(const void *p);
|
||||
|
||||
// Return the linker module, if valid for the platform.
|
||||
LoadedModule *GetLinker();
|
||||
|
||||
// Wrapper for chunk metadata operations.
|
||||
class LsanMetadata {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -89,70 +89,9 @@ void ProcessGlobalRegions(Frontier *frontier) {
|
|||
dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);
|
||||
}
|
||||
|
||||
static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) {
|
||||
CHECK(stack_id);
|
||||
StackTrace stack = map->Get(stack_id);
|
||||
// The top frame is our malloc/calloc/etc. The next frame is the caller.
|
||||
if (stack.size >= 2)
|
||||
return stack.trace[1];
|
||||
return 0;
|
||||
}
|
||||
LoadedModule *GetLinker() { return linker; }
|
||||
|
||||
struct ProcessPlatformAllocParam {
|
||||
Frontier *frontier;
|
||||
StackDepotReverseMap *stack_depot_reverse_map;
|
||||
bool skip_linker_allocations;
|
||||
};
|
||||
|
||||
// ForEachChunk callback. Identifies unreachable chunks which must be treated as
|
||||
// reachable. Marks them as reachable and adds them to the frontier.
|
||||
static void ProcessPlatformSpecificAllocationsCb(uptr chunk, void *arg) {
|
||||
CHECK(arg);
|
||||
ProcessPlatformAllocParam *param =
|
||||
reinterpret_cast<ProcessPlatformAllocParam *>(arg);
|
||||
chunk = GetUserBegin(chunk);
|
||||
LsanMetadata m(chunk);
|
||||
if (m.allocated() && m.tag() != kReachable && m.tag() != kIgnored) {
|
||||
u32 stack_id = m.stack_trace_id();
|
||||
uptr caller_pc = 0;
|
||||
if (stack_id > 0)
|
||||
caller_pc = GetCallerPC(stack_id, param->stack_depot_reverse_map);
|
||||
// If caller_pc is unknown, this chunk may be allocated in a coroutine. Mark
|
||||
// it as reachable, as we can't properly report its allocation stack anyway.
|
||||
if (caller_pc == 0 || (param->skip_linker_allocations &&
|
||||
linker->containsAddress(caller_pc))) {
|
||||
m.set_tag(kReachable);
|
||||
param->frontier->push_back(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handles dynamically allocated TLS blocks by treating all chunks allocated
|
||||
// from ld-linux.so as reachable.
|
||||
// Dynamic TLS blocks contain the TLS variables of dynamically loaded modules.
|
||||
// They are allocated with a __libc_memalign() call in allocate_and_init()
|
||||
// (elf/dl-tls.c). Glibc won't tell us the address ranges occupied by those
|
||||
// blocks, but we can make sure they come from our own allocator by intercepting
|
||||
// __libc_memalign(). On top of that, there is no easy way to reach them. Their
|
||||
// addresses are stored in a dynamically allocated array (the DTV) which is
|
||||
// referenced from the static TLS. Unfortunately, we can't just rely on the DTV
|
||||
// being reachable from the static TLS, and the dynamic TLS being reachable from
|
||||
// the DTV. This is because the initial DTV is allocated before our interception
|
||||
// mechanism kicks in, and thus we don't recognize it as allocated memory. We
|
||||
// can't special-case it either, since we don't know its size.
|
||||
// Our solution is to include in the root set all allocations made from
|
||||
// ld-linux.so (which is where allocate_and_init() is implemented). This is
|
||||
// guaranteed to include all dynamic TLS blocks (and possibly other allocations
|
||||
// which we don't care about).
|
||||
void ProcessPlatformSpecificAllocations(Frontier *frontier) {
|
||||
StackDepotReverseMap stack_depot_reverse_map;
|
||||
ProcessPlatformAllocParam arg;
|
||||
arg.frontier = frontier;
|
||||
arg.stack_depot_reverse_map = &stack_depot_reverse_map;
|
||||
arg.skip_linker_allocations =
|
||||
flags()->use_tls && flags()->use_ld_allocations && linker != nullptr;
|
||||
ForEachChunk(ProcessPlatformSpecificAllocationsCb, &arg);
|
||||
}
|
||||
void ProcessPlatformSpecificAllocations(Frontier *frontier) {}
|
||||
|
||||
struct DoStopTheWorldParam {
|
||||
StopTheWorldCallback callback;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <mach/mach.h>
|
||||
|
||||
namespace __lsan {
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -85,6 +87,8 @@ void SetCurrentThread(u32 tid) { get_tls_val(true)->current_thread_id = tid; }
|
|||
|
||||
AllocatorCache *GetAllocatorCache() { return &get_tls_val(true)->cache; }
|
||||
|
||||
LoadedModule *GetLinker() { return nullptr; }
|
||||
|
||||
// Required on Linux for initialization of TLS behavior, but should not be
|
||||
// required on Darwin.
|
||||
void InitializePlatformSpecificModules() {
|
||||
|
|
@ -106,7 +110,7 @@ void ProcessGlobalRegions(Frontier *frontier) {
|
|||
|
||||
for (const __sanitizer::LoadedModule::AddressRange &range :
|
||||
modules[i].ranges()) {
|
||||
if (range.executable) continue;
|
||||
if (range.executable || !range.readable) continue;
|
||||
|
||||
ScanGlobalRange(range.beg, range.end, frontier);
|
||||
}
|
||||
|
|
@ -114,11 +118,54 @@ void ProcessGlobalRegions(Frontier *frontier) {
|
|||
}
|
||||
|
||||
void ProcessPlatformSpecificAllocations(Frontier *frontier) {
|
||||
CHECK(0 && "unimplemented");
|
||||
mach_port_name_t port;
|
||||
if (task_for_pid(mach_task_self(), internal_getpid(), &port)
|
||||
!= KERN_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned depth = 1;
|
||||
vm_size_t size = 0;
|
||||
vm_address_t address = 0;
|
||||
kern_return_t err = KERN_SUCCESS;
|
||||
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
|
||||
|
||||
InternalMmapVector<RootRegion> const *root_regions = GetRootRegions();
|
||||
|
||||
while (err == KERN_SUCCESS) {
|
||||
struct vm_region_submap_info_64 info;
|
||||
err = vm_region_recurse_64(port, &address, &size, &depth,
|
||||
(vm_region_info_t)&info, &count);
|
||||
|
||||
uptr end_address = address + size;
|
||||
|
||||
// libxpc stashes some pointers in the Kernel Alloc Once page,
|
||||
// make sure not to report those as leaks.
|
||||
if (info.user_tag == VM_MEMORY_OS_ALLOC_ONCE) {
|
||||
ScanRangeForPointers(address, end_address, frontier, "GLOBAL",
|
||||
kReachable);
|
||||
}
|
||||
|
||||
// This additional root region scan is required on Darwin in order to
|
||||
// detect root regions contained within mmap'd memory regions, because
|
||||
// the Darwin implementation of sanitizer_procmaps traverses images
|
||||
// as loaded by dyld, and not the complete set of all memory regions.
|
||||
//
|
||||
// TODO(fjricci) - remove this once sanitizer_procmaps_mac has the same
|
||||
// behavior as sanitizer_procmaps_linux and traverses all memory regions
|
||||
if (flags()->use_root_regions) {
|
||||
for (uptr i = 0; i < root_regions->size(); i++) {
|
||||
ScanRootRegion(frontier, (*root_regions)[i], address, end_address,
|
||||
info.protection);
|
||||
}
|
||||
}
|
||||
|
||||
address = end_address;
|
||||
}
|
||||
}
|
||||
|
||||
void DoStopTheWorld(StopTheWorldCallback callback, void *argument) {
|
||||
CHECK(0 && "unimplemented");
|
||||
StopTheWorld(callback, argument);
|
||||
}
|
||||
|
||||
} // namespace __lsan
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ u32 ThreadCreate(u32 parent_tid, uptr user_id, bool detached) {
|
|||
/* arg */ nullptr);
|
||||
}
|
||||
|
||||
void ThreadStart(u32 tid, uptr os_id) {
|
||||
void ThreadStart(u32 tid, tid_t os_id) {
|
||||
OnStartedArgs args;
|
||||
uptr stack_size = 0;
|
||||
uptr tls_size = 0;
|
||||
|
|
@ -127,7 +127,7 @@ void EnsureMainThreadIDIsCorrect() {
|
|||
|
||||
///// Interface to the common LSan module. /////
|
||||
|
||||
bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
|
||||
bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end,
|
||||
uptr *tls_begin, uptr *tls_end, uptr *cache_begin,
|
||||
uptr *cache_end, DTLS **dtls) {
|
||||
ThreadContext *context = static_cast<ThreadContext *>(
|
||||
|
|
@ -143,7 +143,7 @@ bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
|
|||
return true;
|
||||
}
|
||||
|
||||
void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback,
|
||||
void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback,
|
||||
void *arg) {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class ThreadContext : public ThreadContextBase {
|
|||
|
||||
void InitializeThreadRegistry();
|
||||
|
||||
void ThreadStart(u32 tid, uptr os_id);
|
||||
void ThreadStart(u32 tid, tid_t os_id);
|
||||
void ThreadFinish();
|
||||
u32 ThreadCreate(u32 tid, uptr uid, bool detached);
|
||||
void ThreadJoin(u32 tid);
|
||||
|
|
|
|||
|
|
@ -284,9 +284,10 @@ void LoadedModule::clear() {
|
|||
}
|
||||
}
|
||||
|
||||
void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) {
|
||||
void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable,
|
||||
bool readable) {
|
||||
void *mem = InternalAlloc(sizeof(AddressRange));
|
||||
AddressRange *r = new(mem) AddressRange(beg, end, executable);
|
||||
AddressRange *r = new(mem) AddressRange(beg, end, executable, readable);
|
||||
ranges_.push_back(r);
|
||||
if (executable && end > max_executable_address_)
|
||||
max_executable_address_ = end;
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ INLINE uptr GetPageSizeCached() {
|
|||
uptr GetMmapGranularity();
|
||||
uptr GetMaxVirtualAddress();
|
||||
// Threads
|
||||
uptr GetTid();
|
||||
tid_t GetTid();
|
||||
uptr GetThreadSelf();
|
||||
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
|
||||
uptr *stack_bottom);
|
||||
|
|
@ -717,7 +717,7 @@ class LoadedModule {
|
|||
void set(const char *module_name, uptr base_address, ModuleArch arch,
|
||||
u8 uuid[kModuleUUIDSize], bool instrumented);
|
||||
void clear();
|
||||
void addAddressRange(uptr beg, uptr end, bool executable);
|
||||
void addAddressRange(uptr beg, uptr end, bool executable, bool readable);
|
||||
bool containsAddress(uptr address) const;
|
||||
|
||||
const char *full_name() const { return full_name_; }
|
||||
|
|
@ -732,9 +732,14 @@ class LoadedModule {
|
|||
uptr beg;
|
||||
uptr end;
|
||||
bool executable;
|
||||
bool readable;
|
||||
|
||||
AddressRange(uptr beg, uptr end, bool executable)
|
||||
: next(nullptr), beg(beg), end(end), executable(executable) {}
|
||||
AddressRange(uptr beg, uptr end, bool executable, bool readable)
|
||||
: next(nullptr),
|
||||
beg(beg),
|
||||
end(end),
|
||||
executable(executable),
|
||||
readable(readable) {}
|
||||
};
|
||||
|
||||
const IntrusiveList<AddressRange> &ranges() const { return ranges_; }
|
||||
|
|
|
|||
|
|
@ -139,12 +139,9 @@ bool PlatformHasDifferentMemcpyAndMemmove();
|
|||
#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (0)
|
||||
#endif
|
||||
|
||||
#define COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s, len, n) \
|
||||
COMMON_INTERCEPTOR_READ_RANGE((ctx), (s), \
|
||||
common_flags()->strict_string_checks ? (len) + 1 : (n) )
|
||||
|
||||
#define COMMON_INTERCEPTOR_READ_STRING(ctx, s, n) \
|
||||
COMMON_INTERCEPTOR_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n))
|
||||
COMMON_INTERCEPTOR_READ_RANGE((ctx), (s), \
|
||||
common_flags()->strict_string_checks ? (REAL(strlen)(s)) + 1 : (n) )
|
||||
|
||||
#ifndef COMMON_INTERCEPTOR_ON_DLOPEN
|
||||
#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \
|
||||
|
|
@ -450,8 +447,7 @@ static inline void StrstrCheck(void *ctx, char *r, const char *s1,
|
|||
const char *s2) {
|
||||
uptr len1 = REAL(strlen)(s1);
|
||||
uptr len2 = REAL(strlen)(s2);
|
||||
COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s1, len1,
|
||||
r ? r - s1 + len2 : len1 + 1);
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r ? r - s1 + len2 : len1 + 1);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, len2 + 1);
|
||||
}
|
||||
#endif
|
||||
|
|
@ -577,10 +573,11 @@ INTERCEPTOR(char*, strchr, const char *s, int c) {
|
|||
return internal_strchr(s, c);
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, strchr, s, c);
|
||||
char *result = REAL(strchr)(s, c);
|
||||
uptr len = internal_strlen(s);
|
||||
uptr n = result ? result - s + 1 : len + 1;
|
||||
if (common_flags()->intercept_strchr)
|
||||
COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s, len, n);
|
||||
if (common_flags()->intercept_strchr) {
|
||||
// Keep strlen as macro argument, as macro may ignore it.
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, s,
|
||||
(result ? result - s : REAL(strlen)(s)) + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#define INIT_STRCHR COMMON_INTERCEPT_FUNCTION(strchr)
|
||||
|
|
@ -609,9 +606,8 @@ INTERCEPTOR(char*, strrchr, const char *s, int c) {
|
|||
if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
|
||||
return internal_strrchr(s, c);
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, strrchr, s, c);
|
||||
uptr len = internal_strlen(s);
|
||||
if (common_flags()->intercept_strchr)
|
||||
COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s, len, len + 1);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1);
|
||||
return REAL(strrchr)(s, c);
|
||||
}
|
||||
#define INIT_STRRCHR COMMON_INTERCEPT_FUNCTION(strrchr)
|
||||
|
|
|
|||
|
|
@ -10,21 +10,13 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
INTERFACE_FUNCTION(__sanitizer_cov)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_dump)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_indir_call16)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_init)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_module_init)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_basic_block)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_func_enter)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_with_check)
|
||||
INTERFACE_FUNCTION(__sanitizer_dump_coverage)
|
||||
INTERFACE_FUNCTION(__sanitizer_dump_trace_pc_guard_coverage)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_coverage_guards)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_number_of_counters)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_total_unique_caller_callee_pairs)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage)
|
||||
INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file)
|
||||
INTERFACE_FUNCTION(__sanitizer_reset_coverage)
|
||||
INTERFACE_FUNCTION(__sanitizer_update_counter_bitset_and_clear_counters)
|
||||
INTERFACE_WEAK_FUNCTION(__sancov_default_options)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp1)
|
||||
|
|
|
|||
|
|
@ -57,12 +57,6 @@ static const u64 kMagic = SANITIZER_WORDSIZE == 64 ? kMagic64 : kMagic32;
|
|||
static atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once.
|
||||
|
||||
static atomic_uintptr_t coverage_counter;
|
||||
static atomic_uintptr_t caller_callee_counter;
|
||||
|
||||
static void ResetGlobalCounters() {
|
||||
return atomic_store(&coverage_counter, 0, memory_order_relaxed);
|
||||
return atomic_store(&caller_callee_counter, 0, memory_order_relaxed);
|
||||
}
|
||||
|
||||
// pc_array is the array containing the covered PCs.
|
||||
// To make the pc_array thread- and async-signal-safe it has to be large enough.
|
||||
|
|
@ -90,25 +84,14 @@ class CoverageData {
|
|||
void AfterFork(int child_pid);
|
||||
void Extend(uptr npcs);
|
||||
void Add(uptr pc, u32 *guard);
|
||||
void IndirCall(uptr caller, uptr callee, uptr callee_cache[],
|
||||
uptr cache_size);
|
||||
void DumpCallerCalleePairs();
|
||||
void DumpTrace();
|
||||
void DumpAsBitSet();
|
||||
void DumpCounters();
|
||||
void DumpOffsets();
|
||||
void DumpAll();
|
||||
|
||||
ALWAYS_INLINE
|
||||
void TraceBasicBlock(u32 *id);
|
||||
|
||||
void InitializeGuardArray(s32 *guards);
|
||||
void InitializeGuards(s32 *guards, uptr n, const char *module_name,
|
||||
uptr caller_pc);
|
||||
void InitializeCounters(u8 *counters, uptr n);
|
||||
void ReinitializeGuards();
|
||||
uptr GetNumberOf8bitCounters();
|
||||
uptr Update8bitCounterBitsetAndClearCounters(u8 *bitset);
|
||||
|
||||
uptr *data();
|
||||
uptr size() const;
|
||||
|
|
@ -150,37 +133,6 @@ class CoverageData {
|
|||
InternalMmapVectorNoCtor<NamedPcRange> comp_unit_name_vec;
|
||||
InternalMmapVectorNoCtor<NamedPcRange> module_name_vec;
|
||||
|
||||
struct CounterAndSize {
|
||||
u8 *counters;
|
||||
uptr n;
|
||||
};
|
||||
|
||||
InternalMmapVectorNoCtor<CounterAndSize> counters_vec;
|
||||
uptr num_8bit_counters;
|
||||
|
||||
// Caller-Callee (cc) array, size and current index.
|
||||
static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24);
|
||||
uptr **cc_array;
|
||||
atomic_uintptr_t cc_array_index;
|
||||
atomic_uintptr_t cc_array_size;
|
||||
|
||||
// Tracing event array, size and current pointer.
|
||||
// We record all events (basic block entries) in a global buffer of u32
|
||||
// values. Each such value is the index in pc_array.
|
||||
// So far the tracing is highly experimental:
|
||||
// - not thread-safe;
|
||||
// - does not support long traces;
|
||||
// - not tuned for performance.
|
||||
// Windows doesn't do overcommit (committed virtual memory costs swap), so
|
||||
// programs can't reliably map such large amounts of virtual memory.
|
||||
// TODO(etienneb): Find a way to support coverage of larger executable
|
||||
static const uptr kTrEventArrayMaxSize =
|
||||
(SANITIZER_WORDSIZE == 32 || SANITIZER_WINDOWS) ? 1 << 22 : 1 << 30;
|
||||
u32 *tr_event_array;
|
||||
uptr tr_event_array_size;
|
||||
u32 *tr_event_pointer;
|
||||
static const uptr kTrPcArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 27);
|
||||
|
||||
StaticSpinMutex mu;
|
||||
};
|
||||
|
||||
|
|
@ -217,23 +169,6 @@ void CoverageData::Enable() {
|
|||
} else {
|
||||
atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed);
|
||||
}
|
||||
|
||||
cc_array = reinterpret_cast<uptr **>(MmapNoReserveOrDie(
|
||||
sizeof(uptr *) * kCcArrayMaxSize, "CovInit::cc_array"));
|
||||
atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed);
|
||||
atomic_store(&cc_array_index, 0, memory_order_relaxed);
|
||||
|
||||
// Allocate tr_event_array with a guard page at the end.
|
||||
tr_event_array = reinterpret_cast<u32 *>(MmapNoReserveOrDie(
|
||||
sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + GetMmapGranularity(),
|
||||
"CovInit::tr_event_array"));
|
||||
MprotectNoAccess(
|
||||
reinterpret_cast<uptr>(&tr_event_array[kTrEventArrayMaxSize]),
|
||||
GetMmapGranularity());
|
||||
tr_event_array_size = kTrEventArrayMaxSize;
|
||||
tr_event_pointer = tr_event_array;
|
||||
|
||||
num_8bit_counters = 0;
|
||||
}
|
||||
|
||||
void CoverageData::InitializeGuardArray(s32 *guards) {
|
||||
|
|
@ -251,17 +186,6 @@ void CoverageData::Disable() {
|
|||
UnmapOrDie(pc_array, sizeof(uptr) * kPcArrayMaxSize);
|
||||
pc_array = nullptr;
|
||||
}
|
||||
if (cc_array) {
|
||||
UnmapOrDie(cc_array, sizeof(uptr *) * kCcArrayMaxSize);
|
||||
cc_array = nullptr;
|
||||
}
|
||||
if (tr_event_array) {
|
||||
UnmapOrDie(tr_event_array,
|
||||
sizeof(tr_event_array[0]) * kTrEventArrayMaxSize +
|
||||
GetMmapGranularity());
|
||||
tr_event_array = nullptr;
|
||||
tr_event_pointer = nullptr;
|
||||
}
|
||||
if (pc_fd != kInvalidFd) {
|
||||
CloseFile(pc_fd);
|
||||
pc_fd = kInvalidFd;
|
||||
|
|
@ -341,15 +265,6 @@ void CoverageData::Extend(uptr npcs) {
|
|||
atomic_store(&pc_array_size, size, memory_order_release);
|
||||
}
|
||||
|
||||
void CoverageData::InitializeCounters(u8 *counters, uptr n) {
|
||||
if (!counters) return;
|
||||
CHECK_EQ(reinterpret_cast<uptr>(counters) % 16, 0);
|
||||
n = RoundUpTo(n, 16); // The compiler must ensure that counters is 16-aligned.
|
||||
SpinMutexLock l(&mu);
|
||||
counters_vec.push_back({counters, n});
|
||||
num_8bit_counters += n;
|
||||
}
|
||||
|
||||
void CoverageData::UpdateModuleNameVec(uptr caller_pc, uptr range_beg,
|
||||
uptr range_end) {
|
||||
auto sym = Symbolizer::GetOrInit();
|
||||
|
|
@ -424,98 +339,6 @@ void CoverageData::Add(uptr pc, u32 *guard) {
|
|||
pc_array[idx] = BundlePcAndCounter(pc, counter);
|
||||
}
|
||||
|
||||
// Registers a pair caller=>callee.
|
||||
// When a given caller is seen for the first time, the callee_cache is added
|
||||
// to the global array cc_array, callee_cache[0] is set to caller and
|
||||
// callee_cache[1] is set to cache_size.
|
||||
// Then we are trying to add callee to callee_cache [2,cache_size) if it is
|
||||
// not there yet.
|
||||
// If the cache is full we drop the callee (may want to fix this later).
|
||||
void CoverageData::IndirCall(uptr caller, uptr callee, uptr callee_cache[],
|
||||
uptr cache_size) {
|
||||
if (!cc_array) return;
|
||||
atomic_uintptr_t *atomic_callee_cache =
|
||||
reinterpret_cast<atomic_uintptr_t *>(callee_cache);
|
||||
uptr zero = 0;
|
||||
if (atomic_compare_exchange_strong(&atomic_callee_cache[0], &zero, caller,
|
||||
memory_order_seq_cst)) {
|
||||
uptr idx = atomic_fetch_add(&cc_array_index, 1, memory_order_relaxed);
|
||||
CHECK_LT(idx * sizeof(uptr),
|
||||
atomic_load(&cc_array_size, memory_order_acquire));
|
||||
callee_cache[1] = cache_size;
|
||||
cc_array[idx] = callee_cache;
|
||||
}
|
||||
CHECK_EQ(atomic_load(&atomic_callee_cache[0], memory_order_relaxed), caller);
|
||||
for (uptr i = 2; i < cache_size; i++) {
|
||||
uptr was = 0;
|
||||
if (atomic_compare_exchange_strong(&atomic_callee_cache[i], &was, callee,
|
||||
memory_order_seq_cst)) {
|
||||
atomic_fetch_add(&caller_callee_counter, 1, memory_order_relaxed);
|
||||
return;
|
||||
}
|
||||
if (was == callee) // Already have this callee.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uptr CoverageData::GetNumberOf8bitCounters() {
|
||||
return num_8bit_counters;
|
||||
}
|
||||
|
||||
// Map every 8bit counter to a 8-bit bitset and clear the counter.
|
||||
uptr CoverageData::Update8bitCounterBitsetAndClearCounters(u8 *bitset) {
|
||||
uptr num_new_bits = 0;
|
||||
uptr cur = 0;
|
||||
// For better speed we map 8 counters to 8 bytes of bitset at once.
|
||||
static const uptr kBatchSize = 8;
|
||||
CHECK_EQ(reinterpret_cast<uptr>(bitset) % kBatchSize, 0);
|
||||
for (uptr i = 0, len = counters_vec.size(); i < len; i++) {
|
||||
u8 *c = counters_vec[i].counters;
|
||||
uptr n = counters_vec[i].n;
|
||||
CHECK_EQ(n % 16, 0);
|
||||
CHECK_EQ(cur % kBatchSize, 0);
|
||||
CHECK_EQ(reinterpret_cast<uptr>(c) % kBatchSize, 0);
|
||||
if (!bitset) {
|
||||
internal_bzero_aligned16(c, n);
|
||||
cur += n;
|
||||
continue;
|
||||
}
|
||||
for (uptr j = 0; j < n; j += kBatchSize, cur += kBatchSize) {
|
||||
CHECK_LT(cur, num_8bit_counters);
|
||||
u64 *pc64 = reinterpret_cast<u64*>(c + j);
|
||||
u64 *pb64 = reinterpret_cast<u64*>(bitset + cur);
|
||||
u64 c64 = *pc64;
|
||||
u64 old_bits_64 = *pb64;
|
||||
u64 new_bits_64 = old_bits_64;
|
||||
if (c64) {
|
||||
*pc64 = 0;
|
||||
for (uptr k = 0; k < kBatchSize; k++) {
|
||||
u64 x = (c64 >> (8 * k)) & 0xff;
|
||||
if (x) {
|
||||
u64 bit = 0;
|
||||
/**/ if (x >= 128) bit = 128;
|
||||
else if (x >= 32) bit = 64;
|
||||
else if (x >= 16) bit = 32;
|
||||
else if (x >= 8) bit = 16;
|
||||
else if (x >= 4) bit = 8;
|
||||
else if (x >= 3) bit = 4;
|
||||
else if (x >= 2) bit = 2;
|
||||
else if (x >= 1) bit = 1;
|
||||
u64 mask = bit << (8 * k);
|
||||
if (!(new_bits_64 & mask)) {
|
||||
num_new_bits++;
|
||||
new_bits_64 |= mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
*pb64 = new_bits_64;
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK_EQ(cur, num_8bit_counters);
|
||||
return num_new_bits;
|
||||
}
|
||||
|
||||
uptr *CoverageData::data() {
|
||||
return pc_array;
|
||||
}
|
||||
|
|
@ -596,132 +419,6 @@ static fd_t CovOpenFile(InternalScopedString *path, bool packed,
|
|||
return fd;
|
||||
}
|
||||
|
||||
// Dump trace PCs and trace events into two separate files.
|
||||
void CoverageData::DumpTrace() {
|
||||
uptr max_idx = tr_event_pointer - tr_event_array;
|
||||
if (!max_idx) return;
|
||||
auto sym = Symbolizer::GetOrInit();
|
||||
if (!sym)
|
||||
return;
|
||||
InternalScopedString out(32 << 20);
|
||||
for (uptr i = 0, n = size(); i < n; i++) {
|
||||
const char *module_name = "<unknown>";
|
||||
uptr module_address = 0;
|
||||
sym->GetModuleNameAndOffsetForPC(UnbundlePc(pc_array[i]), &module_name,
|
||||
&module_address);
|
||||
out.append("%s 0x%zx\n", module_name, module_address);
|
||||
}
|
||||
InternalScopedString path(kMaxPathLength);
|
||||
fd_t fd = CovOpenFile(&path, false, "trace-points");
|
||||
if (fd == kInvalidFd) return;
|
||||
WriteToFile(fd, out.data(), out.length());
|
||||
CloseFile(fd);
|
||||
|
||||
fd = CovOpenFile(&path, false, "trace-compunits");
|
||||
if (fd == kInvalidFd) return;
|
||||
out.clear();
|
||||
for (uptr i = 0; i < comp_unit_name_vec.size(); i++)
|
||||
out.append("%s\n", comp_unit_name_vec[i].copied_module_name);
|
||||
WriteToFile(fd, out.data(), out.length());
|
||||
CloseFile(fd);
|
||||
|
||||
fd = CovOpenFile(&path, false, "trace-events");
|
||||
if (fd == kInvalidFd) return;
|
||||
uptr bytes_to_write = max_idx * sizeof(tr_event_array[0]);
|
||||
u8 *event_bytes = reinterpret_cast<u8*>(tr_event_array);
|
||||
// The trace file could be huge, and may not be written with a single syscall.
|
||||
while (bytes_to_write) {
|
||||
uptr actually_written;
|
||||
if (WriteToFile(fd, event_bytes, bytes_to_write, &actually_written) &&
|
||||
actually_written <= bytes_to_write) {
|
||||
bytes_to_write -= actually_written;
|
||||
event_bytes += actually_written;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
CloseFile(fd);
|
||||
VReport(1, " CovDump: Trace: %zd PCs written\n", size());
|
||||
VReport(1, " CovDump: Trace: %zd Events written\n", max_idx);
|
||||
}
|
||||
|
||||
// This function dumps the caller=>callee pairs into a file as a sequence of
|
||||
// lines like "module_name offset".
|
||||
void CoverageData::DumpCallerCalleePairs() {
|
||||
uptr max_idx = atomic_load(&cc_array_index, memory_order_relaxed);
|
||||
if (!max_idx) return;
|
||||
auto sym = Symbolizer::GetOrInit();
|
||||
if (!sym)
|
||||
return;
|
||||
InternalScopedString out(32 << 20);
|
||||
uptr total = 0;
|
||||
for (uptr i = 0; i < max_idx; i++) {
|
||||
uptr *cc_cache = cc_array[i];
|
||||
CHECK(cc_cache);
|
||||
uptr caller = cc_cache[0];
|
||||
uptr n_callees = cc_cache[1];
|
||||
const char *caller_module_name = "<unknown>";
|
||||
uptr caller_module_address = 0;
|
||||
sym->GetModuleNameAndOffsetForPC(caller, &caller_module_name,
|
||||
&caller_module_address);
|
||||
for (uptr j = 2; j < n_callees; j++) {
|
||||
uptr callee = cc_cache[j];
|
||||
if (!callee) break;
|
||||
total++;
|
||||
const char *callee_module_name = "<unknown>";
|
||||
uptr callee_module_address = 0;
|
||||
sym->GetModuleNameAndOffsetForPC(callee, &callee_module_name,
|
||||
&callee_module_address);
|
||||
out.append("%s 0x%zx\n%s 0x%zx\n", caller_module_name,
|
||||
caller_module_address, callee_module_name,
|
||||
callee_module_address);
|
||||
}
|
||||
}
|
||||
InternalScopedString path(kMaxPathLength);
|
||||
fd_t fd = CovOpenFile(&path, false, "caller-callee");
|
||||
if (fd == kInvalidFd) return;
|
||||
WriteToFile(fd, out.data(), out.length());
|
||||
CloseFile(fd);
|
||||
VReport(1, " CovDump: %zd caller-callee pairs written\n", total);
|
||||
}
|
||||
|
||||
// Record the current PC into the event buffer.
|
||||
// Every event is a u32 value (index in tr_pc_array_index) so we compute
|
||||
// it once and then cache in the provided 'cache' storage.
|
||||
//
|
||||
// This function will eventually be inlined by the compiler.
|
||||
void CoverageData::TraceBasicBlock(u32 *id) {
|
||||
// Will trap here if
|
||||
// 1. coverage is not enabled at run-time.
|
||||
// 2. The array tr_event_array is full.
|
||||
*tr_event_pointer = *id - 1;
|
||||
tr_event_pointer++;
|
||||
}
|
||||
|
||||
void CoverageData::DumpCounters() {
|
||||
if (!common_flags()->coverage_counters) return;
|
||||
uptr n = coverage_data.GetNumberOf8bitCounters();
|
||||
if (!n) return;
|
||||
InternalScopedBuffer<u8> bitset(n);
|
||||
coverage_data.Update8bitCounterBitsetAndClearCounters(bitset.data());
|
||||
InternalScopedString path(kMaxPathLength);
|
||||
|
||||
for (uptr m = 0; m < module_name_vec.size(); m++) {
|
||||
auto r = module_name_vec[m];
|
||||
CHECK(r.copied_module_name);
|
||||
CHECK_LE(r.beg, r.end);
|
||||
CHECK_LE(r.end, size());
|
||||
const char *base_name = StripModuleName(r.copied_module_name);
|
||||
fd_t fd =
|
||||
CovOpenFile(&path, /* packed */ false, base_name, "counters-sancov");
|
||||
if (fd == kInvalidFd) return;
|
||||
WriteToFile(fd, bitset.data() + r.beg, r.end - r.beg);
|
||||
CloseFile(fd);
|
||||
VReport(1, " CovDump: %zd counters written for '%s'\n", r.end - r.beg,
|
||||
base_name);
|
||||
}
|
||||
}
|
||||
|
||||
void CoverageData::DumpAsBitSet() {
|
||||
if (!common_flags()->coverage_bitset) return;
|
||||
if (!size()) return;
|
||||
|
|
@ -869,10 +566,7 @@ void CoverageData::DumpAll() {
|
|||
if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed))
|
||||
return;
|
||||
DumpAsBitSet();
|
||||
DumpCounters();
|
||||
DumpTrace();
|
||||
DumpOffsets();
|
||||
DumpCallerCalleePairs();
|
||||
}
|
||||
|
||||
void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
|
||||
|
|
@ -946,11 +640,6 @@ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_with_check(u32 *guard) {
|
|||
coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()),
|
||||
guard);
|
||||
}
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void
|
||||
__sanitizer_cov_indir_call16(uptr callee, uptr callee_cache16[]) {
|
||||
coverage_data.IndirCall(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()),
|
||||
callee, callee_cache16, 16);
|
||||
}
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() {
|
||||
coverage_enabled = true;
|
||||
coverage_dir = common_flags()->coverage_dir;
|
||||
|
|
@ -964,7 +653,6 @@ SANITIZER_INTERFACE_ATTRIBUTE void
|
|||
__sanitizer_cov_module_init(s32 *guards, uptr npcs, u8 *counters,
|
||||
const char *comp_unit_name) {
|
||||
coverage_data.InitializeGuards(guards, npcs, comp_unit_name, GET_CALLER_PC());
|
||||
coverage_data.InitializeCounters(counters, npcs);
|
||||
if (!common_flags()->coverage_direct) return;
|
||||
if (SANITIZER_ANDROID && coverage_enabled) {
|
||||
// dlopen/dlclose interceptors do not work on Android, so we rely on
|
||||
|
|
@ -982,45 +670,6 @@ uptr __sanitizer_get_total_unique_coverage() {
|
|||
return atomic_load(&coverage_counter, memory_order_relaxed);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __sanitizer_get_total_unique_caller_callee_pairs() {
|
||||
return atomic_load(&caller_callee_counter, memory_order_relaxed);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_func_enter(u32 *id) {
|
||||
__sanitizer_cov_with_check(id);
|
||||
coverage_data.TraceBasicBlock(id);
|
||||
}
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_basic_block(u32 *id) {
|
||||
__sanitizer_cov_with_check(id);
|
||||
coverage_data.TraceBasicBlock(id);
|
||||
}
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_reset_coverage() {
|
||||
ResetGlobalCounters();
|
||||
coverage_data.ReinitializeGuards();
|
||||
internal_bzero_aligned16(
|
||||
coverage_data.data(),
|
||||
RoundUpTo(coverage_data.size() * sizeof(coverage_data.data()[0]), 16));
|
||||
}
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __sanitizer_get_coverage_guards(uptr **data) {
|
||||
*data = coverage_data.data();
|
||||
return coverage_data.size();
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __sanitizer_get_number_of_counters() {
|
||||
return coverage_data.GetNumberOf8bitCounters();
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __sanitizer_update_counter_bitset_and_clear_counters(u8 *bitset) {
|
||||
return coverage_data.Update8bitCounterBitsetAndClearCounters(bitset);
|
||||
}
|
||||
|
||||
// Default empty implementations (weak). Users should redefine them.
|
||||
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp, void) {}
|
||||
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp1, void) {}
|
||||
|
|
|
|||
|
|
@ -152,6 +152,12 @@ typedef u32 operator_new_size_type;
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#if SANITIZER_MAC
|
||||
// On Darwin, thread IDs are 64-bit even on 32-bit systems.
|
||||
typedef u64 tid_t;
|
||||
#else
|
||||
typedef uptr tid_t;
|
||||
#endif
|
||||
|
||||
// ----------- ATTENTION -------------
|
||||
// This header should NOT include any other headers to avoid portability issues.
|
||||
|
|
|
|||
|
|
@ -384,7 +384,7 @@ bool FileExists(const char *filename) {
|
|||
return S_ISREG(st.st_mode);
|
||||
}
|
||||
|
||||
uptr GetTid() {
|
||||
tid_t GetTid() {
|
||||
#if SANITIZER_FREEBSD
|
||||
return (uptr)pthread_self();
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -447,7 +447,9 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
|
|||
uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
|
||||
uptr cur_end = cur_beg + phdr->p_memsz;
|
||||
bool executable = phdr->p_flags & PF_X;
|
||||
cur_module.addAddressRange(cur_beg, cur_end, executable);
|
||||
bool readable = phdr->p_flags & PF_R;
|
||||
cur_module.addAddressRange(cur_beg, cur_end, executable,
|
||||
readable);
|
||||
}
|
||||
}
|
||||
data->modules->push_back(cur_module);
|
||||
|
|
|
|||
|
|
@ -252,9 +252,8 @@ bool FileExists(const char *filename) {
|
|||
return S_ISREG(st.st_mode);
|
||||
}
|
||||
|
||||
uptr GetTid() {
|
||||
// FIXME: This can potentially get truncated on 32-bit, where uptr is 4 bytes.
|
||||
uint64_t tid;
|
||||
tid_t GetTid() {
|
||||
tid_t tid;
|
||||
pthread_threadid_np(nullptr, &tid);
|
||||
return tid;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,4 +259,15 @@
|
|||
# define SANITIZER_GO 0
|
||||
#endif
|
||||
|
||||
// On PowerPC and ARM Thumb, calling pthread_exit() causes LSan to detect leaks.
|
||||
// pthread_exit() performs unwinding that leads to dlopen'ing libgcc_s.so.
|
||||
// dlopen mallocs "libgcc_s.so" string which confuses LSan, it fails to realize
|
||||
// that this allocation happens in dynamic linker and should be ignored.
|
||||
#if SANITIZER_PPC || defined(__thumb__)
|
||||
# define SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT 1
|
||||
#else
|
||||
# define SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT 0
|
||||
#endif
|
||||
|
||||
|
||||
#endif // SANITIZER_PLATFORM_H
|
||||
|
|
|
|||
|
|
@ -141,7 +141,8 @@ void MemoryMappingLayout::DumpListOfModules(
|
|||
uptr base_address = (i ? cur_beg : 0) - cur_offset;
|
||||
LoadedModule cur_module;
|
||||
cur_module.set(cur_name, base_address);
|
||||
cur_module.addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
|
||||
cur_module.addAddressRange(cur_beg, cur_end, prot & kProtectionExecute,
|
||||
prot & kProtectionRead);
|
||||
modules->push_back(cur_module);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -262,7 +262,8 @@ void MemoryMappingLayout::DumpListOfModules(
|
|||
cur_module->set(cur_name, cur_beg, cur_arch, cur_uuid,
|
||||
current_instrumented_);
|
||||
}
|
||||
cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
|
||||
cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute,
|
||||
prot & kProtectionRead);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
#include "sanitizer_common.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
typedef int SuspendedThreadID;
|
||||
|
||||
enum PtraceRegistersStatus {
|
||||
REGISTERS_UNAVAILABLE_FATAL = -1,
|
||||
|
|
@ -30,31 +29,21 @@ enum PtraceRegistersStatus {
|
|||
// register contexts.
|
||||
class SuspendedThreadsList {
|
||||
public:
|
||||
SuspendedThreadsList()
|
||||
: thread_ids_(1024) {}
|
||||
SuspendedThreadID GetThreadID(uptr index) const {
|
||||
CHECK_LT(index, thread_ids_.size());
|
||||
return thread_ids_[index];
|
||||
SuspendedThreadsList() = default;
|
||||
|
||||
// Can't declare pure virtual functions in sanitizer runtimes:
|
||||
// __cxa_pure_virtual might be unavailable. Use UNIMPLEMENTED() instead.
|
||||
virtual PtraceRegistersStatus GetRegistersAndSP(uptr index, uptr *buffer,
|
||||
uptr *sp) const {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
PtraceRegistersStatus GetRegistersAndSP(uptr index, uptr *buffer,
|
||||
uptr *sp) const;
|
||||
|
||||
// The buffer in GetRegistersAndSP should be at least this big.
|
||||
static uptr RegisterCount();
|
||||
uptr thread_count() const { return thread_ids_.size(); }
|
||||
bool Contains(SuspendedThreadID thread_id) const {
|
||||
for (uptr i = 0; i < thread_ids_.size(); i++) {
|
||||
if (thread_ids_[i] == thread_id)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void Append(SuspendedThreadID thread_id) {
|
||||
thread_ids_.push_back(thread_id);
|
||||
}
|
||||
virtual uptr RegisterCount() const { UNIMPLEMENTED(); }
|
||||
virtual uptr ThreadCount() const { UNIMPLEMENTED(); }
|
||||
virtual tid_t GetThreadID(uptr index) const { UNIMPLEMENTED(); }
|
||||
|
||||
private:
|
||||
InternalMmapVector<SuspendedThreadID> thread_ids_;
|
||||
|
||||
// Prohibit copy and assign.
|
||||
SuspendedThreadsList(const SuspendedThreadsList&);
|
||||
void operator=(const SuspendedThreadsList&);
|
||||
|
|
|
|||
|
|
@ -32,17 +32,13 @@
|
|||
#include <sys/types.h> // for pid_t
|
||||
#include <sys/uio.h> // for iovec
|
||||
#include <elf.h> // for NT_PRSTATUS
|
||||
#if SANITIZER_ANDROID && defined(__arm__)
|
||||
# include <linux/user.h> // for pt_regs
|
||||
#else
|
||||
# ifdef __aarch64__
|
||||
#if defined(__aarch64__) && !SANITIZER_ANDROID
|
||||
// GLIBC 2.20+ sys/user does not include asm/ptrace.h
|
||||
# include <asm/ptrace.h>
|
||||
# endif
|
||||
# include <sys/user.h> // for user_regs_struct
|
||||
# if SANITIZER_ANDROID && SANITIZER_MIPS
|
||||
# include <asm/reg.h> // for mips SP register in sys/user.h
|
||||
# endif
|
||||
# include <asm/ptrace.h>
|
||||
#endif
|
||||
#include <sys/user.h> // for user_regs_struct
|
||||
#if SANITIZER_ANDROID && SANITIZER_MIPS
|
||||
# include <asm/reg.h> // for mips SP register in sys/user.h
|
||||
#endif
|
||||
#include <sys/wait.h> // for signal-related stuff
|
||||
|
||||
|
|
@ -82,7 +78,22 @@
|
|||
|
||||
namespace __sanitizer {
|
||||
|
||||
COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t));
|
||||
class SuspendedThreadsListLinux : public SuspendedThreadsList {
|
||||
public:
|
||||
SuspendedThreadsListLinux() : thread_ids_(1024) {}
|
||||
|
||||
tid_t GetThreadID(uptr index) const;
|
||||
uptr ThreadCount() const;
|
||||
bool ContainsTid(tid_t thread_id) const;
|
||||
void Append(tid_t tid);
|
||||
|
||||
PtraceRegistersStatus GetRegistersAndSP(uptr index, uptr *buffer,
|
||||
uptr *sp) const;
|
||||
uptr RegisterCount() const;
|
||||
|
||||
private:
|
||||
InternalMmapVector<tid_t> thread_ids_;
|
||||
};
|
||||
|
||||
// Structure for passing arguments into the tracer thread.
|
||||
struct TracerThreadArgument {
|
||||
|
|
@ -107,31 +118,31 @@ class ThreadSuspender {
|
|||
bool SuspendAllThreads();
|
||||
void ResumeAllThreads();
|
||||
void KillAllThreads();
|
||||
SuspendedThreadsList &suspended_threads_list() {
|
||||
SuspendedThreadsListLinux &suspended_threads_list() {
|
||||
return suspended_threads_list_;
|
||||
}
|
||||
TracerThreadArgument *arg;
|
||||
private:
|
||||
SuspendedThreadsList suspended_threads_list_;
|
||||
SuspendedThreadsListLinux suspended_threads_list_;
|
||||
pid_t pid_;
|
||||
bool SuspendThread(SuspendedThreadID thread_id);
|
||||
bool SuspendThread(tid_t thread_id);
|
||||
};
|
||||
|
||||
bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) {
|
||||
bool ThreadSuspender::SuspendThread(tid_t tid) {
|
||||
// Are we already attached to this thread?
|
||||
// Currently this check takes linear time, however the number of threads is
|
||||
// usually small.
|
||||
if (suspended_threads_list_.Contains(tid))
|
||||
return false;
|
||||
if (suspended_threads_list_.ContainsTid(tid)) return false;
|
||||
int pterrno;
|
||||
if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, nullptr, nullptr),
|
||||
&pterrno)) {
|
||||
// Either the thread is dead, or something prevented us from attaching.
|
||||
// Log this event and move on.
|
||||
VReport(1, "Could not attach to thread %d (errno %d).\n", tid, pterrno);
|
||||
VReport(1, "Could not attach to thread %zu (errno %d).\n", (uptr)tid,
|
||||
pterrno);
|
||||
return false;
|
||||
} else {
|
||||
VReport(2, "Attached to thread %d.\n", tid);
|
||||
VReport(2, "Attached to thread %zu.\n", (uptr)tid);
|
||||
// The thread is not guaranteed to stop before ptrace returns, so we must
|
||||
// wait on it. Note: if the thread receives a signal concurrently,
|
||||
// we can get notification about the signal before notification about stop.
|
||||
|
|
@ -149,8 +160,8 @@ bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) {
|
|||
if (internal_iserror(waitpid_status, &wperrno)) {
|
||||
// Got a ECHILD error. I don't think this situation is possible, but it
|
||||
// doesn't hurt to report it.
|
||||
VReport(1, "Waiting on thread %d failed, detaching (errno %d).\n",
|
||||
tid, wperrno);
|
||||
VReport(1, "Waiting on thread %zu failed, detaching (errno %d).\n",
|
||||
(uptr)tid, wperrno);
|
||||
internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -167,7 +178,7 @@ bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) {
|
|||
}
|
||||
|
||||
void ThreadSuspender::ResumeAllThreads() {
|
||||
for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++) {
|
||||
for (uptr i = 0; i < suspended_threads_list_.ThreadCount(); i++) {
|
||||
pid_t tid = suspended_threads_list_.GetThreadID(i);
|
||||
int pterrno;
|
||||
if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr),
|
||||
|
|
@ -183,7 +194,7 @@ void ThreadSuspender::ResumeAllThreads() {
|
|||
}
|
||||
|
||||
void ThreadSuspender::KillAllThreads() {
|
||||
for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++)
|
||||
for (uptr i = 0; i < suspended_threads_list_.ThreadCount(); i++)
|
||||
internal_ptrace(PTRACE_KILL, suspended_threads_list_.GetThreadID(i),
|
||||
nullptr, nullptr);
|
||||
}
|
||||
|
|
@ -494,9 +505,28 @@ typedef _user_regs_struct regs_struct;
|
|||
#error "Unsupported architecture"
|
||||
#endif // SANITIZER_ANDROID && defined(__arm__)
|
||||
|
||||
PtraceRegistersStatus SuspendedThreadsList::GetRegistersAndSP(uptr index,
|
||||
uptr *buffer,
|
||||
uptr *sp) const {
|
||||
tid_t SuspendedThreadsListLinux::GetThreadID(uptr index) const {
|
||||
CHECK_LT(index, thread_ids_.size());
|
||||
return thread_ids_[index];
|
||||
}
|
||||
|
||||
uptr SuspendedThreadsListLinux::ThreadCount() const {
|
||||
return thread_ids_.size();
|
||||
}
|
||||
|
||||
bool SuspendedThreadsListLinux::ContainsTid(tid_t thread_id) const {
|
||||
for (uptr i = 0; i < thread_ids_.size(); i++) {
|
||||
if (thread_ids_[i] == thread_id) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SuspendedThreadsListLinux::Append(tid_t tid) {
|
||||
thread_ids_.push_back(tid);
|
||||
}
|
||||
|
||||
PtraceRegistersStatus SuspendedThreadsListLinux::GetRegistersAndSP(
|
||||
uptr index, uptr *buffer, uptr *sp) const {
|
||||
pid_t tid = GetThreadID(index);
|
||||
regs_struct regs;
|
||||
int pterrno;
|
||||
|
|
@ -526,7 +556,7 @@ PtraceRegistersStatus SuspendedThreadsList::GetRegistersAndSP(uptr index,
|
|||
return REGISTERS_AVAILABLE;
|
||||
}
|
||||
|
||||
uptr SuspendedThreadsList::RegisterCount() {
|
||||
uptr SuspendedThreadsListLinux::RegisterCount() const {
|
||||
return sizeof(regs_struct) / sizeof(uptr);
|
||||
}
|
||||
} // namespace __sanitizer
|
||||
|
|
|
|||
|
|
@ -14,27 +14,169 @@
|
|||
#include "sanitizer_platform.h"
|
||||
|
||||
#if SANITIZER_MAC && (defined(__x86_64__) || defined(__aarch64__) || \
|
||||
defined(__mips64) || defined(__i386))
|
||||
defined(__i386))
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <mach/thread_info.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "sanitizer_stoptheworld.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
typedef struct {
|
||||
tid_t tid;
|
||||
thread_t thread;
|
||||
} SuspendedThreadInfo;
|
||||
|
||||
class SuspendedThreadsListMac : public SuspendedThreadsList {
|
||||
public:
|
||||
SuspendedThreadsListMac() : threads_(1024) {}
|
||||
|
||||
tid_t GetThreadID(uptr index) const;
|
||||
thread_t GetThread(uptr index) const;
|
||||
uptr ThreadCount() const;
|
||||
bool ContainsThread(thread_t thread) const;
|
||||
void Append(thread_t thread);
|
||||
|
||||
PtraceRegistersStatus GetRegistersAndSP(uptr index, uptr *buffer,
|
||||
uptr *sp) const;
|
||||
uptr RegisterCount() const;
|
||||
|
||||
private:
|
||||
InternalMmapVector<SuspendedThreadInfo> threads_;
|
||||
};
|
||||
|
||||
struct RunThreadArgs {
|
||||
StopTheWorldCallback callback;
|
||||
void *argument;
|
||||
};
|
||||
|
||||
void RunThread(void *arg) {
|
||||
struct RunThreadArgs *run_args = (struct RunThreadArgs *)arg;
|
||||
SuspendedThreadsListMac suspended_threads_list;
|
||||
|
||||
mach_port_t task;
|
||||
kern_return_t err = task_for_pid(mach_task_self(), internal_getpid(), &task);
|
||||
if (err != KERN_SUCCESS) {
|
||||
VReport(1, "Getting task from pid failed (errno %d).\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
thread_array_t threads;
|
||||
mach_msg_type_number_t num_threads;
|
||||
|
||||
err = task_threads(task, &threads, &num_threads);
|
||||
if (err != KERN_SUCCESS) {
|
||||
VReport(1, "Failed to get threads for task (errno %d).\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
thread_t thread_self = mach_thread_self();
|
||||
for (unsigned int i = 0; i < num_threads; ++i) {
|
||||
if (threads[i] == thread_self) continue;
|
||||
|
||||
thread_suspend(threads[i]);
|
||||
suspended_threads_list.Append(threads[i]);
|
||||
}
|
||||
|
||||
run_args->callback(suspended_threads_list, run_args->argument);
|
||||
|
||||
uptr num_suspended = suspended_threads_list.ThreadCount();
|
||||
for (unsigned int i = 0; i < num_suspended; ++i) {
|
||||
thread_resume(suspended_threads_list.GetThread(i));
|
||||
}
|
||||
}
|
||||
|
||||
void StopTheWorld(StopTheWorldCallback callback, void *argument) {
|
||||
CHECK(0 && "unimplemented");
|
||||
struct RunThreadArgs arg = {callback, argument};
|
||||
pthread_t run_thread = (pthread_t)internal_start_thread(RunThread, &arg);
|
||||
internal_join_thread(run_thread);
|
||||
}
|
||||
|
||||
PtraceRegistersStatus SuspendedThreadsList::GetRegistersAndSP(uptr index,
|
||||
uptr *buffer,
|
||||
uptr *sp) const {
|
||||
CHECK(0 && "unimplemented");
|
||||
return REGISTERS_UNAVAILABLE_FATAL;
|
||||
#if defined(__x86_64__)
|
||||
typedef x86_thread_state64_t regs_struct;
|
||||
|
||||
#define SP_REG __rsp
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
typedef arm_thread_state64_t regs_struct;
|
||||
|
||||
# if __DARWIN_UNIX03
|
||||
# define SP_REG __sp
|
||||
# else
|
||||
# define SP_REG sp
|
||||
# endif
|
||||
|
||||
#elif defined(__i386)
|
||||
typedef x86_thread_state32_t regs_struct;
|
||||
|
||||
#define SP_REG __esp
|
||||
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#endif
|
||||
|
||||
tid_t SuspendedThreadsListMac::GetThreadID(uptr index) const {
|
||||
CHECK_LT(index, threads_.size());
|
||||
return threads_[index].tid;
|
||||
}
|
||||
|
||||
uptr SuspendedThreadsList::RegisterCount() {
|
||||
CHECK(0 && "unimplemented");
|
||||
return 0;
|
||||
thread_t SuspendedThreadsListMac::GetThread(uptr index) const {
|
||||
CHECK_LT(index, threads_.size());
|
||||
return threads_[index].thread;
|
||||
}
|
||||
|
||||
uptr SuspendedThreadsListMac::ThreadCount() const {
|
||||
return threads_.size();
|
||||
}
|
||||
|
||||
bool SuspendedThreadsListMac::ContainsThread(thread_t thread) const {
|
||||
for (uptr i = 0; i < threads_.size(); i++) {
|
||||
if (threads_[i].thread == thread) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SuspendedThreadsListMac::Append(thread_t thread) {
|
||||
thread_identifier_info_data_t info;
|
||||
mach_msg_type_number_t info_count = THREAD_IDENTIFIER_INFO_COUNT;
|
||||
kern_return_t err = thread_info(thread, THREAD_IDENTIFIER_INFO,
|
||||
(thread_info_t)&info, &info_count);
|
||||
if (err != KERN_SUCCESS) {
|
||||
VReport(1, "Error - unable to get thread ident for a thread\n");
|
||||
return;
|
||||
}
|
||||
threads_.push_back({info.thread_id, thread});
|
||||
}
|
||||
|
||||
PtraceRegistersStatus SuspendedThreadsListMac::GetRegistersAndSP(
|
||||
uptr index, uptr *buffer, uptr *sp) const {
|
||||
thread_t thread = GetThread(index);
|
||||
regs_struct regs;
|
||||
int err;
|
||||
mach_msg_type_number_t reg_count = MACHINE_THREAD_STATE_COUNT;
|
||||
err = thread_get_state(thread, MACHINE_THREAD_STATE, (thread_state_t)®s,
|
||||
®_count);
|
||||
if (err != KERN_SUCCESS) {
|
||||
VReport(1, "Error - unable to get registers for a thread\n");
|
||||
// KERN_INVALID_ARGUMENT indicates that either the flavor is invalid,
|
||||
// or the thread does not exist. The other possible error case,
|
||||
// MIG_ARRAY_TOO_LARGE, means that the state is too large, but it's
|
||||
// still safe to proceed.
|
||||
return err == KERN_INVALID_ARGUMENT ? REGISTERS_UNAVAILABLE_FATAL
|
||||
: REGISTERS_UNAVAILABLE;
|
||||
}
|
||||
|
||||
internal_memcpy(buffer, ®s, sizeof(regs));
|
||||
*sp = regs.SP_REG;
|
||||
|
||||
return REGISTERS_AVAILABLE;
|
||||
}
|
||||
|
||||
uptr SuspendedThreadsListMac::RegisterCount() const {
|
||||
return MACHINE_THREAD_STATE_COUNT;
|
||||
}
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_MAC && (defined(__x86_64__) || defined(__aarch64__)) ||
|
||||
// defined(__mips64) || defined(__i386))
|
||||
// defined(__i386))
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ void ThreadContextBase::SetFinished() {
|
|||
OnFinished();
|
||||
}
|
||||
|
||||
void ThreadContextBase::SetStarted(uptr _os_id, bool _workerthread, void *arg) {
|
||||
void ThreadContextBase::SetStarted(tid_t _os_id, bool _workerthread,
|
||||
void *arg) {
|
||||
status = ThreadStatusRunning;
|
||||
os_id = _os_id;
|
||||
workerthread = _workerthread;
|
||||
|
|
@ -193,7 +194,7 @@ static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx,
|
|||
tctx->status != ThreadStatusDead);
|
||||
}
|
||||
|
||||
ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(uptr os_id) {
|
||||
ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) {
|
||||
return FindThreadContextLocked(FindThreadContextByOsIdCallback,
|
||||
(void *)os_id);
|
||||
}
|
||||
|
|
@ -267,7 +268,7 @@ void ThreadRegistry::FinishThread(u32 tid) {
|
|||
}
|
||||
}
|
||||
|
||||
void ThreadRegistry::StartThread(u32 tid, uptr os_id, bool workerthread,
|
||||
void ThreadRegistry::StartThread(u32 tid, tid_t os_id, bool workerthread,
|
||||
void *arg) {
|
||||
BlockingMutexLock l(&mtx_);
|
||||
running_threads_++;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class ThreadContextBase {
|
|||
const u32 tid; // Thread ID. Main thread should have tid = 0.
|
||||
u64 unique_id; // Unique thread ID.
|
||||
u32 reuse_count; // Number of times this tid was reused.
|
||||
uptr os_id; // PID (used for reporting).
|
||||
tid_t os_id; // PID (used for reporting).
|
||||
uptr user_id; // Some opaque user thread id (e.g. pthread_t).
|
||||
char name[64]; // As annotated by user.
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ class ThreadContextBase {
|
|||
void SetDead();
|
||||
void SetJoined(void *arg);
|
||||
void SetFinished();
|
||||
void SetStarted(uptr _os_id, bool _workerthread, void *arg);
|
||||
void SetStarted(tid_t _os_id, bool _workerthread, void *arg);
|
||||
void SetCreated(uptr _user_id, u64 _unique_id, bool _detached,
|
||||
u32 _parent_tid, void *arg);
|
||||
void Reset();
|
||||
|
|
@ -109,14 +109,14 @@ class ThreadRegistry {
|
|||
// is found.
|
||||
ThreadContextBase *FindThreadContextLocked(FindThreadCallback cb,
|
||||
void *arg);
|
||||
ThreadContextBase *FindThreadContextByOsIDLocked(uptr os_id);
|
||||
ThreadContextBase *FindThreadContextByOsIDLocked(tid_t os_id);
|
||||
|
||||
void SetThreadName(u32 tid, const char *name);
|
||||
void SetThreadNameByUserId(uptr user_id, const char *name);
|
||||
void DetachThread(u32 tid, void *arg);
|
||||
void JoinThread(u32 tid, void *arg);
|
||||
void FinishThread(u32 tid);
|
||||
void StartThread(u32 tid, uptr os_id, bool workerthread, void *arg);
|
||||
void StartThread(u32 tid, tid_t os_id, bool workerthread, void *arg);
|
||||
|
||||
private:
|
||||
const ThreadContextFactory context_factory_;
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ uptr internal_getpid() {
|
|||
|
||||
// In contrast to POSIX, on Windows GetCurrentThreadId()
|
||||
// returns a system-unique identifier.
|
||||
uptr GetTid() {
|
||||
tid_t GetTid() {
|
||||
return GetCurrentThreadId();
|
||||
}
|
||||
|
||||
|
|
@ -553,7 +553,8 @@ void ListOfModules::init() {
|
|||
LoadedModule cur_module;
|
||||
cur_module.set(module_name, adjusted_base);
|
||||
// We add the whole module as one single address range.
|
||||
cur_module.addAddressRange(base_address, end_address, /*executable*/ true);
|
||||
cur_module.addAddressRange(base_address, end_address, /*executable*/ true,
|
||||
/*readable*/ true);
|
||||
modules_.push_back(cur_module);
|
||||
}
|
||||
UnmapOrDie(hmodules, modules_buffer_size);
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@
|
|||
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <string.h>
|
||||
|
||||
namespace __scudo {
|
||||
|
||||
|
|
@ -60,9 +59,9 @@ typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, 0, SizeClassMap,
|
|||
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
|
||||
typedef ScudoLargeMmapAllocator SecondaryAllocator;
|
||||
typedef CombinedAllocator<PrimaryAllocator, AllocatorCache, SecondaryAllocator>
|
||||
ScudoAllocator;
|
||||
ScudoBackendAllocator;
|
||||
|
||||
static ScudoAllocator &getAllocator();
|
||||
static ScudoBackendAllocator &getBackendAllocator();
|
||||
|
||||
static thread_local Xorshift128Plus Prng;
|
||||
// Global static cookie, initialized at start-up.
|
||||
|
|
@ -101,9 +100,10 @@ struct ScudoChunk : UnpackedHeader {
|
|||
// Returns the usable size for a chunk, meaning the amount of bytes from the
|
||||
// beginning of the user data to the end of the backend allocated chunk.
|
||||
uptr getUsableSize(UnpackedHeader *Header) {
|
||||
uptr Size = getAllocator().GetActuallyAllocatedSize(getAllocBeg(Header));
|
||||
uptr Size = getBackendAllocator().GetActuallyAllocatedSize(
|
||||
getAllocBeg(Header));
|
||||
if (Size == 0)
|
||||
return Size;
|
||||
return 0;
|
||||
return Size - AlignedChunkHeaderSize - (Header->Offset << MinAlignmentLog);
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +120,8 @@ struct ScudoChunk : UnpackedHeader {
|
|||
return static_cast<u16>(Crc);
|
||||
}
|
||||
|
||||
// Checks the validity of a chunk by verifying its checksum.
|
||||
// Checks the validity of a chunk by verifying its checksum. It doesn't
|
||||
// incur termination in the event of an invalid chunk.
|
||||
bool isValid() {
|
||||
UnpackedHeader NewUnpackedHeader;
|
||||
const AtomicPackedHeader *AtomicHeader =
|
||||
|
|
@ -130,13 +131,27 @@ struct ScudoChunk : UnpackedHeader {
|
|||
return (NewUnpackedHeader.Checksum == computeChecksum(&NewUnpackedHeader));
|
||||
}
|
||||
|
||||
// Nulls out a chunk header. When returning the chunk to the backend, there
|
||||
// is no need to store a valid ChunkAvailable header, as this would be
|
||||
// computationally expensive. Zeroing out serves the same purpose by making
|
||||
// the header invalid. In the extremely rare event where 0 would be a valid
|
||||
// checksum for the chunk, the state of the chunk is ChunkAvailable anyway.
|
||||
COMPILER_CHECK(ChunkAvailable == 0);
|
||||
void eraseHeader() {
|
||||
PackedHeader NullPackedHeader = 0;
|
||||
AtomicPackedHeader *AtomicHeader =
|
||||
reinterpret_cast<AtomicPackedHeader *>(this);
|
||||
atomic_store_relaxed(AtomicHeader, NullPackedHeader);
|
||||
}
|
||||
|
||||
// Loads and unpacks the header, verifying the checksum in the process.
|
||||
void loadHeader(UnpackedHeader *NewUnpackedHeader) const {
|
||||
const AtomicPackedHeader *AtomicHeader =
|
||||
reinterpret_cast<const AtomicPackedHeader *>(this);
|
||||
PackedHeader NewPackedHeader = atomic_load_relaxed(AtomicHeader);
|
||||
*NewUnpackedHeader = bit_cast<UnpackedHeader>(NewPackedHeader);
|
||||
if (NewUnpackedHeader->Checksum != computeChecksum(NewUnpackedHeader)) {
|
||||
if (UNLIKELY(NewUnpackedHeader->Checksum !=
|
||||
computeChecksum(NewUnpackedHeader))) {
|
||||
dieWithMessage("ERROR: corrupted chunk header at address %p\n", this);
|
||||
}
|
||||
}
|
||||
|
|
@ -160,15 +175,19 @@ struct ScudoChunk : UnpackedHeader {
|
|||
PackedHeader OldPackedHeader = bit_cast<PackedHeader>(*OldUnpackedHeader);
|
||||
AtomicPackedHeader *AtomicHeader =
|
||||
reinterpret_cast<AtomicPackedHeader *>(this);
|
||||
if (!atomic_compare_exchange_strong(AtomicHeader,
|
||||
&OldPackedHeader,
|
||||
NewPackedHeader,
|
||||
memory_order_relaxed)) {
|
||||
if (UNLIKELY(!atomic_compare_exchange_strong(AtomicHeader,
|
||||
&OldPackedHeader,
|
||||
NewPackedHeader,
|
||||
memory_order_relaxed))) {
|
||||
dieWithMessage("ERROR: race on chunk header at address %p\n", this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ScudoChunk *getScudoChunk(uptr UserBeg) {
|
||||
return reinterpret_cast<ScudoChunk *>(UserBeg - AlignedChunkHeaderSize);
|
||||
}
|
||||
|
||||
static bool ScudoInitIsRunning = false;
|
||||
|
||||
static pthread_once_t GlobalInited = PTHREAD_ONCE_INIT;
|
||||
|
|
@ -190,7 +209,7 @@ static void teardownThread(void *p) {
|
|||
return;
|
||||
}
|
||||
drainQuarantine();
|
||||
getAllocator().DestroyCache(&Cache);
|
||||
getBackendAllocator().DestroyCache(&Cache);
|
||||
ThreadTornDown = true;
|
||||
}
|
||||
|
||||
|
|
@ -223,7 +242,7 @@ static void initGlobal() {
|
|||
static void NOINLINE initThread() {
|
||||
pthread_once(&GlobalInited, initGlobal);
|
||||
pthread_setspecific(PThreadKey, reinterpret_cast<void *>(1));
|
||||
getAllocator().InitCache(&Cache);
|
||||
getBackendAllocator().InitCache(&Cache);
|
||||
ThreadInited = true;
|
||||
}
|
||||
|
||||
|
|
@ -235,38 +254,31 @@ struct QuarantineCallback {
|
|||
void Recycle(ScudoChunk *Chunk) {
|
||||
UnpackedHeader Header;
|
||||
Chunk->loadHeader(&Header);
|
||||
if (Header.State != ChunkQuarantine) {
|
||||
if (UNLIKELY(Header.State != ChunkQuarantine)) {
|
||||
dieWithMessage("ERROR: invalid chunk state when recycling address %p\n",
|
||||
Chunk);
|
||||
}
|
||||
Chunk->eraseHeader();
|
||||
void *Ptr = Chunk->getAllocBeg(&Header);
|
||||
getAllocator().Deallocate(Cache_, Ptr);
|
||||
getBackendAllocator().Deallocate(Cache_, Ptr);
|
||||
}
|
||||
|
||||
/// Internal quarantine allocation and deallocation functions.
|
||||
void *Allocate(uptr Size) {
|
||||
// The internal quarantine memory cannot be protected by us. But the only
|
||||
// structures allocated are QuarantineBatch, that are 8KB for x64. So we
|
||||
// will use mmap for those, and given that Deallocate doesn't pass a size
|
||||
// in, we enforce the size of the allocation to be sizeof(QuarantineBatch).
|
||||
// TODO(kostyak): switching to mmap impacts greatly performances, we have
|
||||
// to find another solution
|
||||
// CHECK_EQ(Size, sizeof(QuarantineBatch));
|
||||
// return MmapOrDie(Size, "QuarantineBatch");
|
||||
return getAllocator().Allocate(Cache_, Size, 1, false);
|
||||
// TODO(kostyak): figure out the best way to protect the batches.
|
||||
return getBackendAllocator().Allocate(Cache_, Size, MinAlignment);
|
||||
}
|
||||
|
||||
void Deallocate(void *Ptr) {
|
||||
// UnmapOrDie(Ptr, sizeof(QuarantineBatch));
|
||||
getAllocator().Deallocate(Cache_, Ptr);
|
||||
getBackendAllocator().Deallocate(Cache_, Ptr);
|
||||
}
|
||||
|
||||
AllocatorCache *Cache_;
|
||||
};
|
||||
|
||||
typedef Quarantine<QuarantineCallback, ScudoChunk> ScudoQuarantine;
|
||||
typedef ScudoQuarantine::Cache QuarantineCache;
|
||||
static thread_local QuarantineCache ThreadQuarantineCache;
|
||||
typedef ScudoQuarantine::Cache ScudoQuarantineCache;
|
||||
static thread_local ScudoQuarantineCache ThreadQuarantineCache;
|
||||
|
||||
void AllocatorOptions::setFrom(const Flags *f, const CommonFlags *cf) {
|
||||
MayReturnNull = cf->allocator_may_return_null;
|
||||
|
|
@ -288,11 +300,11 @@ void AllocatorOptions::copyTo(Flags *f, CommonFlags *cf) const {
|
|||
f->ZeroContents = ZeroContents;
|
||||
}
|
||||
|
||||
struct Allocator {
|
||||
struct ScudoAllocator {
|
||||
static const uptr MaxAllowedMallocSize =
|
||||
FIRST_32_SECOND_64(2UL << 30, 1ULL << 40);
|
||||
|
||||
ScudoAllocator BackendAllocator;
|
||||
ScudoBackendAllocator BackendAllocator;
|
||||
ScudoQuarantine AllocatorQuarantine;
|
||||
|
||||
// The fallback caches are used when the thread local caches have been
|
||||
|
|
@ -300,13 +312,13 @@ struct Allocator {
|
|||
// be accessed by different threads.
|
||||
StaticSpinMutex FallbackMutex;
|
||||
AllocatorCache FallbackAllocatorCache;
|
||||
QuarantineCache FallbackQuarantineCache;
|
||||
ScudoQuarantineCache FallbackQuarantineCache;
|
||||
|
||||
bool DeallocationTypeMismatch;
|
||||
bool ZeroContents;
|
||||
bool DeleteSizeMismatch;
|
||||
|
||||
explicit Allocator(LinkerInitialized)
|
||||
explicit ScudoAllocator(LinkerInitialized)
|
||||
: AllocatorQuarantine(LINKER_INITIALIZED),
|
||||
FallbackQuarantineCache(LINKER_INITIALIZED) {}
|
||||
|
||||
|
|
@ -329,14 +341,14 @@ struct Allocator {
|
|||
dieWithMessage("ERROR: the maximum possible offset doesn't fit in the "
|
||||
"header\n");
|
||||
}
|
||||
// Verify that we can fit the maximum amount of unused bytes in the header.
|
||||
// Given that the Secondary fits the allocation to a page, the worst case
|
||||
// scenario happens in the Primary. It will depend on the second to last
|
||||
// and last class sizes, as well as the dynamic base for the Primary. The
|
||||
// following is an over-approximation that works for our needs.
|
||||
uptr MaxUnusedBytes = SizeClassMap::kMaxSize - 1 - AlignedChunkHeaderSize;
|
||||
Header.UnusedBytes = MaxUnusedBytes;
|
||||
if (Header.UnusedBytes != MaxUnusedBytes) {
|
||||
// Verify that we can fit the maximum size or amount of unused bytes in the
|
||||
// header. Given that the Secondary fits the allocation to a page, the worst
|
||||
// case scenario happens in the Primary. It will depend on the second to
|
||||
// last and last class sizes, as well as the dynamic base for the Primary.
|
||||
// The following is an over-approximation that works for our needs.
|
||||
uptr MaxSizeOrUnusedBytes = SizeClassMap::kMaxSize - 1;
|
||||
Header.SizeOrUnusedBytes = MaxSizeOrUnusedBytes;
|
||||
if (Header.SizeOrUnusedBytes != MaxSizeOrUnusedBytes) {
|
||||
dieWithMessage("ERROR: the maximum possible unused bytes doesn't fit in "
|
||||
"the header\n");
|
||||
}
|
||||
|
|
@ -349,37 +361,37 @@ struct Allocator {
|
|||
static_cast<uptr>(Options.QuarantineSizeMb) << 20,
|
||||
static_cast<uptr>(Options.ThreadLocalQuarantineSizeKb) << 10);
|
||||
BackendAllocator.InitCache(&FallbackAllocatorCache);
|
||||
Cookie = Prng.Next();
|
||||
Cookie = Prng.getNext();
|
||||
}
|
||||
|
||||
// Helper function that checks for a valid Scudo chunk.
|
||||
// Helper function that checks for a valid Scudo chunk. nullptr isn't.
|
||||
bool isValidPointer(const void *UserPtr) {
|
||||
if (UNLIKELY(!ThreadInited))
|
||||
initThread();
|
||||
uptr ChunkBeg = reinterpret_cast<uptr>(UserPtr);
|
||||
if (!IsAligned(ChunkBeg, MinAlignment)) {
|
||||
if (!UserPtr)
|
||||
return false;
|
||||
}
|
||||
ScudoChunk *Chunk =
|
||||
reinterpret_cast<ScudoChunk *>(ChunkBeg - AlignedChunkHeaderSize);
|
||||
return Chunk->isValid();
|
||||
uptr UserBeg = reinterpret_cast<uptr>(UserPtr);
|
||||
if (!IsAligned(UserBeg, MinAlignment))
|
||||
return false;
|
||||
return getScudoChunk(UserBeg)->isValid();
|
||||
}
|
||||
|
||||
// Allocates a chunk.
|
||||
void *allocate(uptr Size, uptr Alignment, AllocType Type) {
|
||||
void *allocate(uptr Size, uptr Alignment, AllocType Type,
|
||||
bool ForceZeroContents = false) {
|
||||
if (UNLIKELY(!ThreadInited))
|
||||
initThread();
|
||||
if (!IsPowerOfTwo(Alignment)) {
|
||||
if (UNLIKELY(!IsPowerOfTwo(Alignment))) {
|
||||
dieWithMessage("ERROR: alignment is not a power of 2\n");
|
||||
}
|
||||
if (Alignment > MaxAlignment)
|
||||
return BackendAllocator.ReturnNullOrDieOnBadRequest();
|
||||
if (Alignment < MinAlignment)
|
||||
Alignment = MinAlignment;
|
||||
if (Size == 0)
|
||||
Size = 1;
|
||||
if (Size >= MaxAllowedMallocSize)
|
||||
return BackendAllocator.ReturnNullOrDieOnBadRequest();
|
||||
if (Size == 0)
|
||||
Size = 1;
|
||||
|
||||
uptr NeededSize = RoundUpTo(Size, MinAlignment) + AlignedChunkHeaderSize;
|
||||
if (Alignment > MinAlignment)
|
||||
|
|
@ -395,13 +407,13 @@ struct Allocator {
|
|||
bool FromPrimary = PrimaryAllocator::CanAllocate(NeededSize, MinAlignment);
|
||||
|
||||
void *Ptr;
|
||||
uptr AllocationAlignment = FromPrimary ? MinAlignment : Alignment;
|
||||
if (LIKELY(!ThreadTornDown)) {
|
||||
Ptr = BackendAllocator.Allocate(&Cache, NeededSize,
|
||||
FromPrimary ? MinAlignment : Alignment);
|
||||
Ptr = BackendAllocator.Allocate(&Cache, NeededSize, AllocationAlignment);
|
||||
} else {
|
||||
SpinMutexLock l(&FallbackMutex);
|
||||
Ptr = BackendAllocator.Allocate(&FallbackAllocatorCache, NeededSize,
|
||||
FromPrimary ? MinAlignment : Alignment);
|
||||
AllocationAlignment);
|
||||
}
|
||||
if (!Ptr)
|
||||
return BackendAllocator.ReturnNullOrDieOnOOM();
|
||||
|
|
@ -416,30 +428,34 @@ struct Allocator {
|
|||
NeededSize -= Alignment;
|
||||
}
|
||||
|
||||
uptr ActuallyAllocatedSize = BackendAllocator.GetActuallyAllocatedSize(
|
||||
reinterpret_cast<void *>(AllocBeg));
|
||||
// If requested, we will zero out the entire contents of the returned chunk.
|
||||
if (ZeroContents && FromPrimary)
|
||||
memset(Ptr, 0, ActuallyAllocatedSize);
|
||||
if ((ForceZeroContents || ZeroContents) && FromPrimary)
|
||||
memset(Ptr, 0, BackendAllocator.GetActuallyAllocatedSize(Ptr));
|
||||
|
||||
uptr ChunkBeg = AllocBeg + AlignedChunkHeaderSize;
|
||||
if (!IsAligned(ChunkBeg, Alignment))
|
||||
ChunkBeg = RoundUpTo(ChunkBeg, Alignment);
|
||||
CHECK_LE(ChunkBeg + Size, AllocBeg + NeededSize);
|
||||
ScudoChunk *Chunk =
|
||||
reinterpret_cast<ScudoChunk *>(ChunkBeg - AlignedChunkHeaderSize);
|
||||
uptr UserBeg = AllocBeg + AlignedChunkHeaderSize;
|
||||
if (!IsAligned(UserBeg, Alignment))
|
||||
UserBeg = RoundUpTo(UserBeg, Alignment);
|
||||
CHECK_LE(UserBeg + Size, AllocBeg + NeededSize);
|
||||
UnpackedHeader Header = {};
|
||||
Header.State = ChunkAllocated;
|
||||
uptr Offset = ChunkBeg - AlignedChunkHeaderSize - AllocBeg;
|
||||
uptr Offset = UserBeg - AlignedChunkHeaderSize - AllocBeg;
|
||||
Header.Offset = Offset >> MinAlignmentLog;
|
||||
Header.AllocType = Type;
|
||||
Header.UnusedBytes = ActuallyAllocatedSize - Offset -
|
||||
AlignedChunkHeaderSize - Size;
|
||||
Header.Salt = static_cast<u8>(Prng.Next());
|
||||
Chunk->storeHeader(&Header);
|
||||
void *UserPtr = reinterpret_cast<void *>(ChunkBeg);
|
||||
// TODO(kostyak): hooks sound like a terrible idea security wise but might
|
||||
// be needed for things to work properly?
|
||||
if (FromPrimary) {
|
||||
Header.FromPrimary = FromPrimary;
|
||||
Header.SizeOrUnusedBytes = Size;
|
||||
} else {
|
||||
// The secondary fits the allocations to a page, so the amount of unused
|
||||
// bytes is the difference between the end of the user allocation and the
|
||||
// next page boundary.
|
||||
uptr PageSize = GetPageSizeCached();
|
||||
uptr TrailingBytes = (UserBeg + Size) & (PageSize - 1);
|
||||
if (TrailingBytes)
|
||||
Header.SizeOrUnusedBytes = PageSize - TrailingBytes;
|
||||
}
|
||||
Header.Salt = static_cast<u8>(Prng.getNext());
|
||||
getScudoChunk(UserBeg)->storeHeader(&Header);
|
||||
void *UserPtr = reinterpret_cast<void *>(UserBeg);
|
||||
// if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(UserPtr, Size);
|
||||
return UserPtr;
|
||||
}
|
||||
|
|
@ -449,53 +465,57 @@ struct Allocator {
|
|||
void deallocate(void *UserPtr, uptr DeleteSize, AllocType Type) {
|
||||
if (UNLIKELY(!ThreadInited))
|
||||
initThread();
|
||||
// TODO(kostyak): see hook comment above
|
||||
// if (&__sanitizer_free_hook) __sanitizer_free_hook(UserPtr);
|
||||
if (!UserPtr)
|
||||
return;
|
||||
uptr ChunkBeg = reinterpret_cast<uptr>(UserPtr);
|
||||
if (!IsAligned(ChunkBeg, MinAlignment)) {
|
||||
uptr UserBeg = reinterpret_cast<uptr>(UserPtr);
|
||||
if (UNLIKELY(!IsAligned(UserBeg, MinAlignment))) {
|
||||
dieWithMessage("ERROR: attempted to deallocate a chunk not properly "
|
||||
"aligned at address %p\n", UserPtr);
|
||||
}
|
||||
ScudoChunk *Chunk =
|
||||
reinterpret_cast<ScudoChunk *>(ChunkBeg - AlignedChunkHeaderSize);
|
||||
ScudoChunk *Chunk = getScudoChunk(UserBeg);
|
||||
UnpackedHeader OldHeader;
|
||||
Chunk->loadHeader(&OldHeader);
|
||||
if (OldHeader.State != ChunkAllocated) {
|
||||
if (UNLIKELY(OldHeader.State != ChunkAllocated)) {
|
||||
dieWithMessage("ERROR: invalid chunk state when deallocating address "
|
||||
"%p\n", UserPtr);
|
||||
}
|
||||
uptr UsableSize = Chunk->getUsableSize(&OldHeader);
|
||||
UnpackedHeader NewHeader = OldHeader;
|
||||
NewHeader.State = ChunkQuarantine;
|
||||
Chunk->compareExchangeHeader(&NewHeader, &OldHeader);
|
||||
if (DeallocationTypeMismatch) {
|
||||
// The deallocation type has to match the allocation one.
|
||||
if (NewHeader.AllocType != Type) {
|
||||
if (OldHeader.AllocType != Type) {
|
||||
// With the exception of memalign'd Chunks, that can be still be free'd.
|
||||
if (NewHeader.AllocType != FromMemalign || Type != FromMalloc) {
|
||||
if (OldHeader.AllocType != FromMemalign || Type != FromMalloc) {
|
||||
dieWithMessage("ERROR: allocation type mismatch on address %p\n",
|
||||
Chunk);
|
||||
UserPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
uptr Size = UsableSize - OldHeader.UnusedBytes;
|
||||
uptr Size = OldHeader.FromPrimary ? OldHeader.SizeOrUnusedBytes :
|
||||
Chunk->getUsableSize(&OldHeader) - OldHeader.SizeOrUnusedBytes;
|
||||
if (DeleteSizeMismatch) {
|
||||
if (DeleteSize && DeleteSize != Size) {
|
||||
dieWithMessage("ERROR: invalid sized delete on chunk at address %p\n",
|
||||
Chunk);
|
||||
UserPtr);
|
||||
}
|
||||
}
|
||||
|
||||
UnpackedHeader NewHeader = OldHeader;
|
||||
NewHeader.State = ChunkQuarantine;
|
||||
Chunk->compareExchangeHeader(&NewHeader, &OldHeader);
|
||||
|
||||
// If a small memory amount was allocated with a larger alignment, we want
|
||||
// to take that into account. Otherwise the Quarantine would be filled with
|
||||
// tiny chunks, taking a lot of VA memory. This an approximation of the
|
||||
// usable size, that allows us to not call GetActuallyAllocatedSize.
|
||||
uptr LiableSize = Size + (OldHeader.Offset << MinAlignment);
|
||||
if (LIKELY(!ThreadTornDown)) {
|
||||
AllocatorQuarantine.Put(&ThreadQuarantineCache,
|
||||
QuarantineCallback(&Cache), Chunk, UsableSize);
|
||||
QuarantineCallback(&Cache), Chunk, LiableSize);
|
||||
} else {
|
||||
SpinMutexLock l(&FallbackMutex);
|
||||
AllocatorQuarantine.Put(&FallbackQuarantineCache,
|
||||
QuarantineCallback(&FallbackAllocatorCache),
|
||||
Chunk, UsableSize);
|
||||
Chunk, LiableSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -504,24 +524,30 @@ struct Allocator {
|
|||
void *reallocate(void *OldPtr, uptr NewSize) {
|
||||
if (UNLIKELY(!ThreadInited))
|
||||
initThread();
|
||||
uptr ChunkBeg = reinterpret_cast<uptr>(OldPtr);
|
||||
ScudoChunk *Chunk =
|
||||
reinterpret_cast<ScudoChunk *>(ChunkBeg - AlignedChunkHeaderSize);
|
||||
uptr UserBeg = reinterpret_cast<uptr>(OldPtr);
|
||||
if (UNLIKELY(!IsAligned(UserBeg, MinAlignment))) {
|
||||
dieWithMessage("ERROR: attempted to reallocate a chunk not properly "
|
||||
"aligned at address %p\n", OldPtr);
|
||||
}
|
||||
ScudoChunk *Chunk = getScudoChunk(UserBeg);
|
||||
UnpackedHeader OldHeader;
|
||||
Chunk->loadHeader(&OldHeader);
|
||||
if (OldHeader.State != ChunkAllocated) {
|
||||
if (UNLIKELY(OldHeader.State != ChunkAllocated)) {
|
||||
dieWithMessage("ERROR: invalid chunk state when reallocating address "
|
||||
"%p\n", OldPtr);
|
||||
}
|
||||
uptr Size = Chunk->getUsableSize(&OldHeader);
|
||||
if (OldHeader.AllocType != FromMalloc) {
|
||||
if (UNLIKELY(OldHeader.AllocType != FromMalloc)) {
|
||||
dieWithMessage("ERROR: invalid chunk type when reallocating address %p\n",
|
||||
Chunk);
|
||||
OldPtr);
|
||||
}
|
||||
uptr UsableSize = Chunk->getUsableSize(&OldHeader);
|
||||
UnpackedHeader NewHeader = OldHeader;
|
||||
// The new size still fits in the current chunk.
|
||||
if (NewSize <= Size) {
|
||||
NewHeader.UnusedBytes = Size - NewSize;
|
||||
// The new size still fits in the current chunk, and the size difference
|
||||
// is reasonable.
|
||||
if (NewSize <= UsableSize &&
|
||||
(UsableSize - NewSize) < (SizeClassMap::kMaxSize / 2)) {
|
||||
NewHeader.SizeOrUnusedBytes =
|
||||
OldHeader.FromPrimary ? NewSize : UsableSize - NewSize;
|
||||
Chunk->compareExchangeHeader(&NewHeader, &OldHeader);
|
||||
return OldPtr;
|
||||
}
|
||||
|
|
@ -529,18 +555,19 @@ struct Allocator {
|
|||
// old one.
|
||||
void *NewPtr = allocate(NewSize, MinAlignment, FromMalloc);
|
||||
if (NewPtr) {
|
||||
uptr OldSize = Size - OldHeader.UnusedBytes;
|
||||
uptr OldSize = OldHeader.FromPrimary ? OldHeader.SizeOrUnusedBytes :
|
||||
UsableSize - OldHeader.SizeOrUnusedBytes;
|
||||
memcpy(NewPtr, OldPtr, Min(NewSize, OldSize));
|
||||
NewHeader.State = ChunkQuarantine;
|
||||
Chunk->compareExchangeHeader(&NewHeader, &OldHeader);
|
||||
if (LIKELY(!ThreadTornDown)) {
|
||||
AllocatorQuarantine.Put(&ThreadQuarantineCache,
|
||||
QuarantineCallback(&Cache), Chunk, Size);
|
||||
QuarantineCallback(&Cache), Chunk, UsableSize);
|
||||
} else {
|
||||
SpinMutexLock l(&FallbackMutex);
|
||||
AllocatorQuarantine.Put(&FallbackQuarantineCache,
|
||||
QuarantineCallback(&FallbackAllocatorCache),
|
||||
Chunk, Size);
|
||||
Chunk, UsableSize);
|
||||
}
|
||||
}
|
||||
return NewPtr;
|
||||
|
|
@ -552,13 +579,12 @@ struct Allocator {
|
|||
initThread();
|
||||
if (!Ptr)
|
||||
return 0;
|
||||
uptr ChunkBeg = reinterpret_cast<uptr>(Ptr);
|
||||
ScudoChunk *Chunk =
|
||||
reinterpret_cast<ScudoChunk *>(ChunkBeg - AlignedChunkHeaderSize);
|
||||
uptr UserBeg = reinterpret_cast<uptr>(Ptr);
|
||||
ScudoChunk *Chunk = getScudoChunk(UserBeg);
|
||||
UnpackedHeader Header;
|
||||
Chunk->loadHeader(&Header);
|
||||
// Getting the usable size of a chunk only makes sense if it's allocated.
|
||||
if (Header.State != ChunkAllocated) {
|
||||
if (UNLIKELY(Header.State != ChunkAllocated)) {
|
||||
dieWithMessage("ERROR: invalid chunk state when sizing address %p\n",
|
||||
Ptr);
|
||||
}
|
||||
|
|
@ -569,13 +595,9 @@ struct Allocator {
|
|||
if (UNLIKELY(!ThreadInited))
|
||||
initThread();
|
||||
uptr Total = NMemB * Size;
|
||||
if (Size != 0 && Total / Size != NMemB) // Overflow check
|
||||
if (Size != 0 && Total / Size != NMemB) // Overflow check
|
||||
return BackendAllocator.ReturnNullOrDieOnBadRequest();
|
||||
void *Ptr = allocate(Total, MinAlignment, FromMalloc);
|
||||
// If ZeroContents, the content of the chunk has already been zero'd out.
|
||||
if (!ZeroContents && Ptr && BackendAllocator.FromPrimary(Ptr))
|
||||
memset(Ptr, 0, getUsableSize(Ptr));
|
||||
return Ptr;
|
||||
return allocate(Total, MinAlignment, FromMalloc, true);
|
||||
}
|
||||
|
||||
void drainQuarantine() {
|
||||
|
|
@ -592,9 +614,9 @@ struct Allocator {
|
|||
}
|
||||
};
|
||||
|
||||
static Allocator Instance(LINKER_INITIALIZED);
|
||||
static ScudoAllocator Instance(LINKER_INITIALIZED);
|
||||
|
||||
static ScudoAllocator &getAllocator() {
|
||||
static ScudoBackendAllocator &getBackendAllocator() {
|
||||
return Instance.BackendAllocator;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,19 +41,20 @@ enum ChunkState : u8 {
|
|||
// using functions such as GetBlockBegin, that is fairly costly. Our first
|
||||
// implementation used the MetaData as well, which offers the advantage of
|
||||
// being stored away from the chunk itself, but accessing it was costly as
|
||||
// well. The header will be atomically loaded and stored using the 16-byte
|
||||
// primitives offered by the platform (likely requires cmpxchg16b support).
|
||||
// well. The header will be atomically loaded and stored.
|
||||
typedef u64 PackedHeader;
|
||||
struct UnpackedHeader {
|
||||
u64 Checksum : 16;
|
||||
u64 UnusedBytes : 20; // Needed for reallocation purposes.
|
||||
u64 State : 2; // available, allocated, or quarantined
|
||||
u64 AllocType : 2; // malloc, new, new[], or memalign
|
||||
u64 Offset : 16; // Offset from the beginning of the backend
|
||||
// allocation to the beginning of the chunk itself,
|
||||
// in multiples of MinAlignment. See comment about
|
||||
// its maximum value and test in init().
|
||||
u64 Salt : 8;
|
||||
u64 Checksum : 16;
|
||||
u64 SizeOrUnusedBytes : 19; // Size for Primary backed allocations, amount of
|
||||
// unused bytes in the chunk for Secondary ones.
|
||||
u64 FromPrimary : 1;
|
||||
u64 State : 2; // available, allocated, or quarantined
|
||||
u64 AllocType : 2; // malloc, new, new[], or memalign
|
||||
u64 Offset : 16; // Offset from the beginning of the backend
|
||||
// allocation to the beginning of the chunk
|
||||
// itself, in multiples of MinAlignment. See
|
||||
/// comment about its maximum value and in init().
|
||||
u64 Salt : 8;
|
||||
};
|
||||
|
||||
typedef atomic_uint64_t AtomicPackedHeader;
|
||||
|
|
|
|||
|
|
@ -88,8 +88,11 @@ class ScudoLargeMmapAllocator {
|
|||
// The primary adds the whole class size to the stats when allocating a
|
||||
// chunk, so we will do something similar here. But we will not account for
|
||||
// the guard pages.
|
||||
Stats->Add(AllocatorStatAllocated, MapSize - 2 * PageSize);
|
||||
Stats->Add(AllocatorStatMapped, MapSize - 2 * PageSize);
|
||||
{
|
||||
SpinMutexLock l(&StatsMutex);
|
||||
Stats->Add(AllocatorStatAllocated, MapSize - 2 * PageSize);
|
||||
Stats->Add(AllocatorStatMapped, MapSize - 2 * PageSize);
|
||||
}
|
||||
|
||||
return reinterpret_cast<void *>(UserBeg);
|
||||
}
|
||||
|
|
@ -112,8 +115,11 @@ class ScudoLargeMmapAllocator {
|
|||
|
||||
void Deallocate(AllocatorStats *Stats, void *Ptr) {
|
||||
SecondaryHeader *Header = getHeader(Ptr);
|
||||
Stats->Sub(AllocatorStatAllocated, Header->MapSize - 2 * PageSize);
|
||||
Stats->Sub(AllocatorStatMapped, Header->MapSize - 2 * PageSize);
|
||||
{
|
||||
SpinMutexLock l(&StatsMutex);
|
||||
Stats->Sub(AllocatorStatAllocated, Header->MapSize - 2 * PageSize);
|
||||
Stats->Sub(AllocatorStatMapped, Header->MapSize - 2 * PageSize);
|
||||
}
|
||||
UnmapOrDie(reinterpret_cast<void *>(Header->MapBeg), Header->MapSize);
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +133,7 @@ class ScudoLargeMmapAllocator {
|
|||
|
||||
uptr GetActuallyAllocatedSize(void *Ptr) {
|
||||
SecondaryHeader *Header = getHeader(Ptr);
|
||||
// Deduct PageSize as MapEnd includes the trailing guard page.
|
||||
// Deduct PageSize as MapSize includes the trailing guard page.
|
||||
uptr MapEnd = Header->MapBeg + Header->MapSize - PageSize;
|
||||
return MapEnd - reinterpret_cast<uptr>(Ptr);
|
||||
}
|
||||
|
|
@ -182,6 +188,7 @@ class ScudoLargeMmapAllocator {
|
|||
const uptr SecondaryHeaderSize = sizeof(SecondaryHeader);
|
||||
const uptr HeadersSize = SecondaryHeaderSize + AlignedChunkHeaderSize;
|
||||
uptr PageSize;
|
||||
SpinMutex StatsMutex;
|
||||
atomic_uint8_t MayReturnNull;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -159,58 +159,4 @@ Xorshift128Plus::Xorshift128Plus() {
|
|||
fillRandom(reinterpret_cast<u8 *>(State), sizeof(State));
|
||||
}
|
||||
|
||||
const static u32 CRC32Table[] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
u32 computeSoftwareCRC32(u32 Crc, uptr Data) {
|
||||
for (uptr i = 0; i < sizeof(Data); i++) {
|
||||
Crc = CRC32Table[(Crc ^ Data) & 0xff] ^ (Crc >> 8);
|
||||
Data >>= 8;
|
||||
}
|
||||
return Crc;
|
||||
}
|
||||
|
||||
} // namespace __scudo
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ bool testCPUFeature(CPUFeature feature);
|
|||
struct Xorshift128Plus {
|
||||
public:
|
||||
Xorshift128Plus();
|
||||
u64 Next() {
|
||||
u64 getNext() {
|
||||
u64 x = State[0];
|
||||
const u64 y = State[1];
|
||||
State[0] = y;
|
||||
|
|
@ -58,7 +58,59 @@ enum : u8 {
|
|||
CRC32Hardware = 1,
|
||||
};
|
||||
|
||||
u32 computeSoftwareCRC32(u32 Crc, uptr Data);
|
||||
const static u32 CRC32Table[] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
INLINE u32 computeSoftwareCRC32(u32 Crc, uptr Data) {
|
||||
for (uptr i = 0; i < sizeof(Data); i++) {
|
||||
Crc = CRC32Table[(Crc ^ Data) & 0xff] ^ (Crc >> 8);
|
||||
Data >>= 8;
|
||||
}
|
||||
return Crc;
|
||||
}
|
||||
|
||||
} // namespace __scudo
|
||||
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ int __tsan_get_report_mutex(void *report, uptr idx, uptr *mutex_id, void **addr,
|
|||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __tsan_get_report_thread(void *report, uptr idx, int *tid, uptr *os_id,
|
||||
int __tsan_get_report_thread(void *report, uptr idx, int *tid, tid_t *os_id,
|
||||
int *running, const char **name, int *parent_tid,
|
||||
void **trace, uptr trace_size) {
|
||||
const ReportDesc *rep = (ReportDesc *)report;
|
||||
|
|
@ -228,7 +228,7 @@ const char *__tsan_locate_address(uptr addr, char *name, uptr name_size,
|
|||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __tsan_get_alloc_stack(uptr addr, uptr *trace, uptr size, int *thread_id,
|
||||
uptr *os_id) {
|
||||
tid_t *os_id) {
|
||||
MBlock *b = 0;
|
||||
Allocator *a = allocator();
|
||||
if (a->PointerIsMine((void *)addr)) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <sanitizer_common/sanitizer_internal_defs.h>
|
||||
using __sanitizer::uptr;
|
||||
using __sanitizer::tid_t;
|
||||
|
||||
// This header should NOT include any other headers.
|
||||
// All functions in this header are extern "C" and start with __tsan_.
|
||||
|
|
@ -143,7 +144,7 @@ int __tsan_get_report_mutex(void *report, uptr idx, uptr *mutex_id, void **addr,
|
|||
|
||||
// Returns information about threads included in the report.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __tsan_get_report_thread(void *report, uptr idx, int *tid, uptr *os_id,
|
||||
int __tsan_get_report_thread(void *report, uptr idx, int *tid, tid_t *os_id,
|
||||
int *running, const char **name, int *parent_tid,
|
||||
void **trace, uptr trace_size);
|
||||
|
||||
|
|
@ -160,7 +161,7 @@ const char *__tsan_locate_address(uptr addr, char *name, uptr name_size,
|
|||
// Returns the allocation stack for a heap pointer.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __tsan_get_alloc_stack(uptr addr, uptr *trace, uptr size, int *thread_id,
|
||||
uptr *os_id);
|
||||
tid_t *os_id);
|
||||
|
||||
#endif // SANITIZER_GO
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ struct ReportLocation {
|
|||
|
||||
struct ReportThread {
|
||||
int id;
|
||||
uptr os_id;
|
||||
tid_t os_id;
|
||||
bool running;
|
||||
bool workerthread;
|
||||
char *name;
|
||||
|
|
|
|||
|
|
@ -720,7 +720,7 @@ void FuncEntry(ThreadState *thr, uptr pc);
|
|||
void FuncExit(ThreadState *thr);
|
||||
|
||||
int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached);
|
||||
void ThreadStart(ThreadState *thr, int tid, uptr os_id, bool workerthread);
|
||||
void ThreadStart(ThreadState *thr, int tid, tid_t os_id, bool workerthread);
|
||||
void ThreadFinish(ThreadState *thr);
|
||||
int ThreadTid(ThreadState *thr, uptr pc, uptr uid);
|
||||
void ThreadJoin(ThreadState *thr, uptr pc, int tid);
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
|
|||
return tid;
|
||||
}
|
||||
|
||||
void ThreadStart(ThreadState *thr, int tid, uptr os_id, bool workerthread) {
|
||||
void ThreadStart(ThreadState *thr, int tid, tid_t os_id, bool workerthread) {
|
||||
uptr stk_addr = 0;
|
||||
uptr stk_size = 0;
|
||||
uptr tls_addr = 0;
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ void InitializeFlags() {
|
|||
CommonFlags cf;
|
||||
cf.CopyFrom(*common_flags());
|
||||
cf.print_summary = false;
|
||||
cf.external_symbolizer_path = GetEnv("UBSAN_SYMBOLIZER_PATH");
|
||||
OverrideCommonFlags(cf);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,11 +118,15 @@ XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
|
|||
return Result;
|
||||
}
|
||||
|
||||
// Test for required CPU features and cache the cycle frequency
|
||||
static bool TSCSupported = probeRequiredCPUFeatures();
|
||||
static uint64_t CycleFrequency = TSCSupported ? getTSCFrequency()
|
||||
: __xray::NanosecondsPerSecond;
|
||||
|
||||
XRayFileHeader Header;
|
||||
Header.Version = 1;
|
||||
Header.Type = FileTypes::FDR_LOG;
|
||||
Header.CycleFrequency = probeRequiredCPUFeatures()
|
||||
? getTSCFrequency() : __xray::NanosecondsPerSecond;
|
||||
Header.CycleFrequency = CycleFrequency;
|
||||
// FIXME: Actually check whether we have 'constant_tsc' and 'nonstop_tsc'
|
||||
// before setting the values in the header.
|
||||
Header.ConstantTSC = 1;
|
||||
|
|
@ -196,7 +200,10 @@ void fdrLoggingHandleArg0(int32_t FuncId,
|
|||
unsigned char CPU;
|
||||
uint64_t TSC;
|
||||
|
||||
if(probeRequiredCPUFeatures()) {
|
||||
// Test once for required CPU features
|
||||
static bool TSCSupported = probeRequiredCPUFeatures();
|
||||
|
||||
if(TSCSupported) {
|
||||
TSC = __xray::readTSC(CPU);
|
||||
} else {
|
||||
// FIXME: This code needs refactoring as it appears in multiple locations
|
||||
|
|
|
|||
|
|
@ -79,15 +79,19 @@ static int __xray_OpenLogFile() XRAY_NEVER_INSTRUMENT {
|
|||
int F = getLogFD();
|
||||
if (F == -1)
|
||||
return -1;
|
||||
|
||||
// Test for required CPU features and cache the cycle frequency
|
||||
static bool TSCSupported = probeRequiredCPUFeatures();
|
||||
static uint64_t CycleFrequency = TSCSupported ? getTSCFrequency()
|
||||
: __xray::NanosecondsPerSecond;
|
||||
|
||||
// Since we're here, we get to write the header. We set it up so that the
|
||||
// header will only be written once, at the start, and let the threads
|
||||
// logging do writes which just append.
|
||||
XRayFileHeader Header;
|
||||
Header.Version = 1;
|
||||
Header.Type = FileTypes::NAIVE_LOG;
|
||||
Header.CycleFrequency = probeRequiredCPUFeatures()
|
||||
? getTSCFrequency()
|
||||
: __xray::NanosecondsPerSecond;
|
||||
Header.CycleFrequency = CycleFrequency;
|
||||
|
||||
// FIXME: Actually check whether we have 'constant_tsc' and 'nonstop_tsc'
|
||||
// before setting the values in the header.
|
||||
|
|
|
|||
|
|
@ -16,41 +16,48 @@
|
|||
#include "../builtins/assembly.h"
|
||||
|
||||
.macro SAVE_REGISTERS
|
||||
subq $200, %rsp
|
||||
movupd %xmm0, 184(%rsp)
|
||||
movupd %xmm1, 168(%rsp)
|
||||
movupd %xmm2, 152(%rsp)
|
||||
movupd %xmm3, 136(%rsp)
|
||||
movupd %xmm4, 120(%rsp)
|
||||
movupd %xmm5, 104(%rsp)
|
||||
movupd %xmm6, 88(%rsp)
|
||||
movupd %xmm7, 72(%rsp)
|
||||
movq %rdi, 64(%rsp)
|
||||
movq %rax, 56(%rsp)
|
||||
movq %rdx, 48(%rsp)
|
||||
movq %rsi, 40(%rsp)
|
||||
movq %rcx, 32(%rsp)
|
||||
movq %r8, 24(%rsp)
|
||||
movq %r9, 16(%rsp)
|
||||
subq $192, %rsp
|
||||
.cfi_def_cfa_offset 200
|
||||
// At this point, the stack pointer should be aligned to an 8-byte boundary,
|
||||
// because any call instructions that come after this will add another 8
|
||||
// bytes and therefore align it to 16-bytes.
|
||||
movq %rbp, 184(%rsp)
|
||||
movupd %xmm0, 168(%rsp)
|
||||
movupd %xmm1, 152(%rsp)
|
||||
movupd %xmm2, 136(%rsp)
|
||||
movupd %xmm3, 120(%rsp)
|
||||
movupd %xmm4, 104(%rsp)
|
||||
movupd %xmm5, 88(%rsp)
|
||||
movupd %xmm6, 72(%rsp)
|
||||
movupd %xmm7, 56(%rsp)
|
||||
movq %rdi, 48(%rsp)
|
||||
movq %rax, 40(%rsp)
|
||||
movq %rdx, 32(%rsp)
|
||||
movq %rsi, 24(%rsp)
|
||||
movq %rcx, 16(%rsp)
|
||||
movq %r8, 8(%rsp)
|
||||
movq %r9, 0(%rsp)
|
||||
.endm
|
||||
|
||||
.macro RESTORE_REGISTERS
|
||||
movupd 184(%rsp), %xmm0
|
||||
movupd 168(%rsp), %xmm1
|
||||
movupd 152(%rsp), %xmm2
|
||||
movupd 136(%rsp), %xmm3
|
||||
movupd 120(%rsp), %xmm4
|
||||
movupd 104(%rsp), %xmm5
|
||||
movupd 88(%rsp) , %xmm6
|
||||
movupd 72(%rsp) , %xmm7
|
||||
movq 64(%rsp), %rdi
|
||||
movq 56(%rsp), %rax
|
||||
movq 48(%rsp), %rdx
|
||||
movq 40(%rsp), %rsi
|
||||
movq 32(%rsp), %rcx
|
||||
movq 24(%rsp), %r8
|
||||
movq 16(%rsp), %r9
|
||||
addq $200, %rsp
|
||||
movq 184(%rsp), %rbp
|
||||
movupd 168(%rsp), %xmm0
|
||||
movupd 152(%rsp), %xmm1
|
||||
movupd 136(%rsp), %xmm2
|
||||
movupd 120(%rsp), %xmm3
|
||||
movupd 104(%rsp), %xmm4
|
||||
movupd 88(%rsp), %xmm5
|
||||
movupd 72(%rsp) , %xmm6
|
||||
movupd 56(%rsp) , %xmm7
|
||||
movq 48(%rsp), %rdi
|
||||
movq 40(%rsp), %rax
|
||||
movq 32(%rsp), %rdx
|
||||
movq 24(%rsp), %rsi
|
||||
movq 16(%rsp), %rcx
|
||||
movq 8(%rsp), %r8
|
||||
movq 0(%rsp), %r9
|
||||
addq $192, %rsp
|
||||
.cfi_def_cfa_offset 8
|
||||
.endm
|
||||
|
||||
.text
|
||||
|
|
@ -64,8 +71,6 @@
|
|||
|
||||
__xray_FunctionEntry:
|
||||
.cfi_startproc
|
||||
pushq %rbp
|
||||
.cfi_def_cfa_offset 16
|
||||
SAVE_REGISTERS
|
||||
|
||||
// This load has to be atomic, it's concurrent with __xray_patch().
|
||||
|
|
@ -80,7 +85,6 @@ __xray_FunctionEntry:
|
|||
callq *%rax
|
||||
.Ltmp0:
|
||||
RESTORE_REGISTERS
|
||||
popq %rbp
|
||||
retq
|
||||
.Ltmp1:
|
||||
.size __xray_FunctionEntry, .Ltmp1-__xray_FunctionEntry
|
||||
|
|
@ -96,14 +100,13 @@ __xray_FunctionExit:
|
|||
// Save the important registers first. Since we're assuming that this
|
||||
// function is only jumped into, we only preserve the registers for
|
||||
// returning.
|
||||
pushq %rbp
|
||||
.cfi_def_cfa_offset 16
|
||||
subq $56, %rsp
|
||||
.cfi_def_cfa_offset 32
|
||||
movupd %xmm0, 40(%rsp)
|
||||
movupd %xmm1, 24(%rsp)
|
||||
movq %rax, 16(%rsp)
|
||||
movq %rdx, 8(%rsp)
|
||||
.cfi_def_cfa_offset 64
|
||||
movq %rbp, 48(%rsp)
|
||||
movupd %xmm0, 32(%rsp)
|
||||
movupd %xmm1, 16(%rsp)
|
||||
movq %rax, 8(%rsp)
|
||||
movq %rdx, 0(%rsp)
|
||||
movq _ZN6__xray19XRayPatchedFunctionE(%rip), %rax
|
||||
testq %rax,%rax
|
||||
je .Ltmp2
|
||||
|
|
@ -113,12 +116,13 @@ __xray_FunctionExit:
|
|||
callq *%rax
|
||||
.Ltmp2:
|
||||
// Restore the important registers.
|
||||
movupd 40(%rsp), %xmm0
|
||||
movupd 24(%rsp), %xmm1
|
||||
movq 16(%rsp), %rax
|
||||
movq 8(%rsp), %rdx
|
||||
movq 48(%rsp), %rbp
|
||||
movupd 32(%rsp), %xmm0
|
||||
movupd 16(%rsp), %xmm1
|
||||
movq 8(%rsp), %rax
|
||||
movq 0(%rsp), %rdx
|
||||
addq $56, %rsp
|
||||
popq %rbp
|
||||
.cfi_def_cfa_offset 8
|
||||
retq
|
||||
.Ltmp3:
|
||||
.size __xray_FunctionExit, .Ltmp3-__xray_FunctionExit
|
||||
|
|
@ -135,8 +139,6 @@ __xray_FunctionTailExit:
|
|||
// this is an exit. In the future, we will introduce a new entry type that
|
||||
// differentiates between a normal exit and a tail exit, but we'd have to do
|
||||
// this and increment the version number for the header.
|
||||
pushq %rbp
|
||||
.cfi_def_cfa_offset 16
|
||||
SAVE_REGISTERS
|
||||
|
||||
movq _ZN6__xray19XRayPatchedFunctionE(%rip), %rax
|
||||
|
|
@ -149,7 +151,6 @@ __xray_FunctionTailExit:
|
|||
|
||||
.Ltmp4:
|
||||
RESTORE_REGISTERS
|
||||
popq %rbp
|
||||
retq
|
||||
.Ltmp5:
|
||||
.size __xray_FunctionTailExit, .Ltmp5-__xray_FunctionTailExit
|
||||
|
|
@ -162,8 +163,6 @@ __xray_FunctionTailExit:
|
|||
.type __xray_ArgLoggerEntry,@function
|
||||
__xray_ArgLoggerEntry:
|
||||
.cfi_startproc
|
||||
pushq %rbp
|
||||
.cfi_def_cfa_offset 16
|
||||
SAVE_REGISTERS
|
||||
|
||||
// Again, these function pointer loads must be atomic; MOV is fine.
|
||||
|
|
@ -184,7 +183,6 @@ __xray_ArgLoggerEntry:
|
|||
|
||||
.Larg1entryFail:
|
||||
RESTORE_REGISTERS
|
||||
popq %rbp
|
||||
retq
|
||||
|
||||
.Larg1entryEnd:
|
||||
|
|
|
|||
|
|
@ -214,6 +214,12 @@ bool probeRequiredCPUFeatures() XRAY_NEVER_INSTRUMENT {
|
|||
Report("Missing rdtscp support.\n");
|
||||
return false;
|
||||
}
|
||||
// Also check whether we can determine the CPU frequency, since if we cannot,
|
||||
// we should use the emulated TSC instead.
|
||||
if (!getTSCFrequency()) {
|
||||
Report("Unable to determine CPU frequency.\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,75 +0,0 @@
|
|||
// Test caller-callee coverage with large number of threads
|
||||
// and various numbers of callers and callees.
|
||||
|
||||
// RUN: %clangxx_asan -fsanitize-coverage=edge,indirect-calls %s -o %t
|
||||
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 10 1 2>&1 | FileCheck %s --check-prefix=CHECK-10-1
|
||||
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 9 2 2>&1 | FileCheck %s --check-prefix=CHECK-9-2
|
||||
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 7 3 2>&1 | FileCheck %s --check-prefix=CHECK-7-3
|
||||
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 17 1 2>&1 | FileCheck %s --check-prefix=CHECK-17-1
|
||||
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 15 2 2>&1 | FileCheck %s --check-prefix=CHECK-15-2
|
||||
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 18 3 2>&1 | FileCheck %s --check-prefix=CHECK-18-3
|
||||
// RUN: rm -f caller-callee*.sancov
|
||||
//
|
||||
// REQUIRES: asan-64-bits
|
||||
// UNSUPPORTED: android
|
||||
//
|
||||
// CHECK-10-1: CovDump: 10 caller-callee pairs written
|
||||
// CHECK-9-2: CovDump: 18 caller-callee pairs written
|
||||
// CHECK-7-3: CovDump: 21 caller-callee pairs written
|
||||
// CHECK-17-1: CovDump: 14 caller-callee pairs written
|
||||
// CHECK-15-2: CovDump: 28 caller-callee pairs written
|
||||
// CHECK-18-3: CovDump: 42 caller-callee pairs written
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
int P = 0;
|
||||
struct Foo {virtual void f() {if (P) printf("Foo::f()\n");}};
|
||||
struct Foo1 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo2 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo3 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo4 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo5 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo6 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo7 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo8 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo9 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo10 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo11 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo12 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo13 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo14 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo15 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo16 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo17 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo18 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo19 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
|
||||
Foo *foo[20] = {
|
||||
new Foo, new Foo1, new Foo2, new Foo3, new Foo4, new Foo5, new Foo6,
|
||||
new Foo7, new Foo8, new Foo9, new Foo10, new Foo11, new Foo12, new Foo13,
|
||||
new Foo14, new Foo15, new Foo16, new Foo17, new Foo18, new Foo19,
|
||||
};
|
||||
|
||||
int n_functions = 10;
|
||||
int n_callers = 2;
|
||||
|
||||
void *Thread(void *arg) {
|
||||
if (n_callers >= 1) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f();
|
||||
if (n_callers >= 2) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f();
|
||||
if (n_callers >= 3) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f();
|
||||
return arg;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc >= 2)
|
||||
n_functions = atoi(argv[1]);
|
||||
if (argc >= 3)
|
||||
n_callers = atoi(argv[2]);
|
||||
const int kNumThreads = 16;
|
||||
pthread_t t[kNumThreads];
|
||||
for (int i = 0; i < kNumThreads; i++)
|
||||
pthread_create(&t[i], 0, Thread, 0);
|
||||
for (int i = 0; i < kNumThreads; i++)
|
||||
pthread_join(t[i], 0);
|
||||
}
|
||||
36
test/asan/TestCases/Posix/strchr.c
Normal file
36
test/asan/TestCases/Posix/strchr.c
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Test strchr for strict_string_checks=false does not look beyond necessary
|
||||
// char.
|
||||
// RUN: %clang_asan %s -o %t
|
||||
// RUN: %env_asan_opts=strict_string_checks=false %run %t 2>&1
|
||||
// RUN: %env_asan_opts=strict_string_checks=true not %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
size_t page_size = sysconf(_SC_PAGE_SIZE);
|
||||
size_t size = 2 * page_size;
|
||||
char *s = (char *)mmap(0, size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
assert(s);
|
||||
assert(((uintptr_t)s & (page_size - 1)) == 0);
|
||||
memset(s, 'o', size);
|
||||
s[size - 1] = 0;
|
||||
|
||||
char *p = s + page_size - 1;
|
||||
*p = 'x';
|
||||
|
||||
if (mprotect(p + 1, 1, PROT_NONE))
|
||||
return 1;
|
||||
char *r = strchr(s, 'x');
|
||||
// CHECK: AddressSanitizer: SEGV on unknown address
|
||||
// CHECK: The signal is caused by a READ memory access
|
||||
// CHECK: strchr.c:[[@LINE-3]]
|
||||
assert(r == p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
// Test __sanitizer_get_total_unique_coverage for caller-callee coverage
|
||||
|
||||
// RUN: %clangxx_asan -fsanitize-coverage=edge,indirect-calls %s -o %t
|
||||
// RUN: %env_asan_opts=coverage=1 %run %t
|
||||
// RUN: rm -f caller-callee*.sancov
|
||||
//
|
||||
// REQUIRES: asan-64-bits
|
||||
|
||||
#include <sanitizer/coverage_interface.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
int P = 0;
|
||||
struct Foo {virtual void f() {if (P) printf("Foo::f()\n");}};
|
||||
struct Foo1 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
struct Foo2 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
|
||||
|
||||
Foo *foo[3] = {new Foo, new Foo1, new Foo2};
|
||||
|
||||
uintptr_t CheckNewTotalUniqueCoverageIsLargerAndReturnIt(uintptr_t old_total) {
|
||||
uintptr_t new_total = __sanitizer_get_total_unique_caller_callee_pairs();
|
||||
fprintf(stderr, "Caller-Callee: old %zd new %zd\n", old_total, new_total);
|
||||
assert(new_total > old_total);
|
||||
return new_total;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uintptr_t total = __sanitizer_get_total_unique_caller_callee_pairs();
|
||||
foo[0]->f();
|
||||
total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total);
|
||||
foo[1]->f();
|
||||
total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total);
|
||||
foo[2]->f();
|
||||
total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total);
|
||||
// Ok, called every function once.
|
||||
// Now call them again from another call site. Should get new coverage.
|
||||
foo[0]->f();
|
||||
total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total);
|
||||
foo[1]->f();
|
||||
total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total);
|
||||
foo[2]->f();
|
||||
total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total);
|
||||
}
|
||||
|
|
@ -8,8 +8,6 @@
|
|||
// RUN: %env_asan_opts=coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3
|
||||
// RUN: %clangxx_asan -O1 -fsanitize-coverage=edge -mllvm -sanitizer-coverage-block-threshold=0 %s -o %t
|
||||
// RUN: %env_asan_opts=coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3
|
||||
// RUN: %clangxx_asan -O1 -fsanitize-coverage=edge,8bit-counters %s -o %t
|
||||
// RUN: %env_asan_opts=coverage=1:coverage_counters=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK_COUNTERS
|
||||
|
||||
// RUN: %env_asan_opts=coverage=1:coverage_bitset=0:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3_NOBITSET
|
||||
// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3_NOBITSET
|
||||
|
|
@ -31,4 +29,3 @@ int main(int argc, char **argv) {
|
|||
// CHECK3: 2 PCs written
|
||||
// CHECK3_NOBITSET-NOT: bitset of
|
||||
// CHECK3_NOPCS-NOT: PCs written
|
||||
// CHECK_COUNTERS: CovDump: 3 counters written for
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
// Test __sanitizer_reset_coverage().
|
||||
|
||||
// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t
|
||||
// RUN: %env_asan_opts=coverage=1 %run %t
|
||||
|
||||
// https://github.com/google/sanitizers/issues/618
|
||||
// UNSUPPORTED: android
|
||||
|
||||
#include <sanitizer/coverage_interface.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
static volatile int sink;
|
||||
__attribute__((noinline)) void bar() { sink = 2; }
|
||||
__attribute__((noinline)) void foo() { sink = 1; }
|
||||
|
||||
// In MSVC 2015, printf is an inline function, which causes this test to fail as
|
||||
// it introduces an extra coverage point. Define away printf on that platform to
|
||||
// avoid the issue.
|
||||
#if _MSC_VER >= 1900
|
||||
# define printf(arg, ...)
|
||||
#endif
|
||||
|
||||
#define GET_AND_PRINT_COVERAGE() \
|
||||
bitset = 0; \
|
||||
for (size_t i = 0; i < n_guards; i++) \
|
||||
if (guards[i]) bitset |= 1U << i; \
|
||||
printf("line %d: bitset %zd total: %zd\n", __LINE__, bitset, \
|
||||
__sanitizer_get_total_unique_coverage());
|
||||
|
||||
#define IS_POWER_OF_TWO(a) ((a & ((a) - 1)) == 0)
|
||||
|
||||
int main() {
|
||||
size_t *guards = 0;
|
||||
size_t bitset;
|
||||
size_t n_guards = __sanitizer_get_coverage_guards(&guards);
|
||||
|
||||
GET_AND_PRINT_COVERAGE();
|
||||
size_t main_bit = bitset;
|
||||
assert(IS_POWER_OF_TWO(main_bit));
|
||||
|
||||
foo();
|
||||
GET_AND_PRINT_COVERAGE();
|
||||
size_t foo_bit = bitset & ~main_bit;
|
||||
assert(IS_POWER_OF_TWO(foo_bit));
|
||||
|
||||
bar();
|
||||
GET_AND_PRINT_COVERAGE();
|
||||
size_t bar_bit = bitset & ~(main_bit | foo_bit);
|
||||
assert(IS_POWER_OF_TWO(bar_bit));
|
||||
|
||||
__sanitizer_reset_coverage();
|
||||
assert(__sanitizer_get_total_unique_coverage() == 0);
|
||||
GET_AND_PRINT_COVERAGE();
|
||||
assert(bitset == 0);
|
||||
|
||||
foo();
|
||||
GET_AND_PRINT_COVERAGE();
|
||||
assert(bitset == foo_bit);
|
||||
|
||||
bar();
|
||||
GET_AND_PRINT_COVERAGE();
|
||||
assert(bitset == (foo_bit | bar_bit));
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
// Test -fsanitize-coverage=trace-bb
|
||||
//
|
||||
// RUN: %clangxx_asan -O1 -fsanitize-coverage=func,trace-bb %s -o %t
|
||||
// RUN: rm -rf %T/coverage-tracing
|
||||
// RUN: mkdir %T/coverage-tracing
|
||||
// RUN: cd %T/coverage-tracing
|
||||
// RUN: A=x; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK1; mv trace-points.*.sancov $A.points
|
||||
// RUN: A=f; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK2; mv trace-points.*.sancov $A.points
|
||||
// RUN: A=b; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK2; mv trace-points.*.sancov $A.points
|
||||
// RUN: A=bf; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK3; mv trace-points.*.sancov $A.points
|
||||
// RUN: A=fb; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK3; mv trace-points.*.sancov $A.points
|
||||
// RUN: A=ffb; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK4; mv trace-points.*.sancov $A.points
|
||||
// RUN: A=fff; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK4; mv trace-points.*.sancov $A.points
|
||||
// RUN: A=bbf; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 100 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK301; mv trace-points.*.sancov $A.points
|
||||
// RUN: diff f.points fff.points
|
||||
// RUN: diff bf.points fb.points
|
||||
// RUN: diff bf.points ffb.points
|
||||
// RUN: diff bf.points bbf.points
|
||||
// RUN: not diff x.points f.points
|
||||
// RUN: not diff x.points b.points
|
||||
// RUN: not diff x.points bf.points
|
||||
// RUN: not diff f.points b.points
|
||||
// RUN: not diff f.points bf.points
|
||||
// RUN: not diff b.points bf.points
|
||||
// RUN: rm -rf %T/coverage-tracing
|
||||
//
|
||||
// REQUIRES: asan-64-bits, shell
|
||||
// UNSUPPORTED: android
|
||||
|
||||
#include <stdlib.h>
|
||||
volatile int sink;
|
||||
__attribute__((noinline)) void foo() { sink++; }
|
||||
__attribute__((noinline)) void bar() { sink++; }
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc != 3) return 0;
|
||||
int n = strtol(argv[2], 0, 10);
|
||||
while (n-- > 0) {
|
||||
for (int i = 0; argv[1][i]; i++) {
|
||||
if (argv[1][i] == 'f') foo();
|
||||
else if (argv[1][i] == 'b') bar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK: CovDump: Trace: 3 PCs written
|
||||
// CHECK1: CovDump: Trace: 1 Events written
|
||||
// CHECK2: CovDump: Trace: 2 Events written
|
||||
// CHECK3: CovDump: Trace: 3 Events written
|
||||
// CHECK4: CovDump: Trace: 4 Events written
|
||||
// CHECK301: CovDump: Trace: 301 Events written
|
||||
|
|
@ -5,8 +5,8 @@
|
|||
// makes its best effort.
|
||||
// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0"
|
||||
// RUN: %clang_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=1 %run %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=0 not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:use_tls=1 %run %t
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:use_tls=0 not %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Regression test. Disabler should not depend on TSD validity.
|
||||
// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=1:use_ld_allocations=0"
|
||||
// RUN: %clang_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE %run %t
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// Test that dynamically allocated TLS space is included in the root set.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0:use_ld_allocations=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0:use_ld_allocations=0"
|
||||
// RUN: %clangxx %s -DBUILD_DSO -fPIC -shared -o %t-so.so
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %run %t 2>&1
|
||||
// RUN: LSAN_OPTIONS="" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=1" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts="" %run %t 2>&1
|
||||
// UNSUPPORTED: i386-linux,i686-linux,arm
|
||||
|
||||
#ifndef BUILD_DSO
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Test that dynamically allocated thread-specific storage is included in the root set.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %run %t 2>&1
|
||||
// RUN: LSAN_OPTIONS="" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=1" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts="" %run %t 2>&1
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Test that statically allocated thread-specific storage is included in the root set.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %run %t 2>&1
|
||||
// RUN: LSAN_OPTIONS="" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=1" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts="" %run %t 2>&1
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Test that statically allocated TLS space is included in the root set.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %run %t 2>&1
|
||||
// RUN: LSAN_OPTIONS="" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=1" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts="" %run %t 2>&1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Test for __lsan_disable() / __lsan_enable().
|
||||
// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0"
|
||||
// RUN: %clang_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Test for ScopedDisabler.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// Test for __lsan_do_leak_check(). We test it by making the leak check run
|
||||
// before global destructors, which also tests compatibility with HeapChecker's
|
||||
// "normal" mode (LSan runs in "strict" mode by default).
|
||||
// RUN: LSAN_BASE="detect_leaks=1:use_stacks=0:use_registers=0"
|
||||
// RUN: LSAN_BASE="use_stacks=0:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck --check-prefix=CHECK-strict %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck --check-prefix=CHECK-normal %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck --check-prefix=CHECK-strict %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE not %run %t foo 2>&1 | FileCheck --check-prefix=CHECK-normal %s
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// A benchmark that executes malloc/free pairs in parallel.
|
||||
// Usage: ./a.out number_of_threads total_number_of_allocations
|
||||
// RUN: LSAN_BASE="detect_leaks=1:use_ld_allocations=0"
|
||||
// RUN: LSAN_BASE="use_ld_allocations=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 5 1000000 2>&1
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE %run %t 5 1000000 2>&1
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Test for __lsan_ignore_object().
|
||||
// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0"
|
||||
// RUN: %clang_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Test for incorrect use of __lsan_ignore_object().
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Test that LargeMmapAllocator's chunks aren't reachable via some internal data structure.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
|
||||
|
||||
// For 32 bit LSan it's pretty likely that large chunks are "reachable" from some
|
||||
// internal data structures (e.g. Glibc global data).
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// Test for the leak_check_at_exit flag.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:use_stacks=0:use_registers=0"
|
||||
// RUN: LSAN_BASE="use_stacks=0:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-do
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"leak_check_at_exit=0" not %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"leak_check_at_exit=0" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-do
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"leak_check_at_exit=0" not %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"leak_check_at_exit=0" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Regression test for http://llvm.org/bugs/show_bug.cgi?id=21621
|
||||
// This test relies on timing between threads, so any failures will be flaky.
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS="log_pointers=1:log_threads=1" %run %t
|
||||
// RUN: %env_lsan_opts="log_pointers=1:log_threads=1" %run %t
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Test for disabling LSan at link-time.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:use_stacks=0:use_registers=0"
|
||||
// RUN: LSAN_BASE="use_stacks=0:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE %run %t
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s
|
||||
|
||||
#include <sanitizer/lsan_interface.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Regression test: pointers to self should not confuse LSan into thinking the
|
||||
// object is indirectly leaked. Only external pointers count.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_registers=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
// Print matched suppressions only if print_suppressions=1 AND at least one is
|
||||
// matched. Default is print_suppressions=true.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:use_registers=0:use_stacks=0"
|
||||
// RUN: LSAN_BASE="use_registers=0:use_stacks=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:print_suppressions=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:print_suppressions=0 %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-print
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:print_suppressions=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:print_suppressions=0 %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-print
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Test for on-demand leak checking.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:use_stacks=0:use_registers=0"
|
||||
// RUN: LSAN_BASE="use_stacks=0:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t foo 2>&1 | FileCheck %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE %run %t foo 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Test for __lsan_(un)register_root_region().
|
||||
// RUN: LSAN_BASE="detect_leaks=1:use_stacks=0:use_registers=0"
|
||||
// RUN: LSAN_BASE="use_stacks=0:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:use_root_regions=0 not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE %run %t
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:use_root_regions=0 not %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Test that out-of-scope local variables are ignored by LSan.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_registers=0:use_stacks=1"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=1"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE":exitcode=0" %run %t 2>&1 | FileCheck --check-prefix=CHECK-sanity %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE":exitcode=0" %run %t 2>&1 | FileCheck --check-prefix=CHECK-sanity %s
|
||||
//
|
||||
// x86 passes parameters through stack that may lead to false negatives
|
||||
// UNSUPPORTED: x86
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// RUN: LSAN_BASE="detect_leaks=1:use_registers=0:use_stacks=0"
|
||||
// RUN: LSAN_BASE="use_registers=0:use_stacks=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
// RUN: LSAN_BASE="detect_leaks=1:use_registers=0:use_stacks=0"
|
||||
// RUN: LSAN_BASE="use_registers=0:use_stacks=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
|
||||
// RUN: rm -f %t.supp
|
||||
// RUN: touch %t.supp
|
||||
// RUN: LSAN_OPTIONS="$LSAN_BASE:suppressions='%t.supp'" not %run %t 2>&1 | FileCheck %s --check-prefix=NOSUPP
|
||||
// RUN: %env_lsan_opts="$LSAN_BASE:suppressions='%t.supp'" not %run %t 2>&1 | FileCheck %s --check-prefix=NOSUPP
|
||||
|
||||
// RUN: echo "leak:*LSanTestLeakingFunc*" > %t.supp
|
||||
// RUN: LSAN_OPTIONS="$LSAN_BASE:suppressions='%t.supp'" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts="$LSAN_BASE:suppressions='%t.supp'" not %run %t 2>&1 | FileCheck %s
|
||||
|
||||
// RUN: echo "leak:%t" > %t.supp
|
||||
// RUN: LSAN_OPTIONS="$LSAN_BASE:suppressions='%t.supp':symbolize=false" %run %t
|
||||
// RUN: %env_lsan_opts="$LSAN_BASE:suppressions='%t.supp':symbolize=false" %run %t
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@
|
|||
// memory. Make sure we don't report these leaks.
|
||||
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_BASE="detect_leaks=1"
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 2>&1
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts= %run %t 2>&1
|
||||
// RUN: %env_lsan_opts= not %run %t foo 2>&1 | FileCheck %s
|
||||
// UNSUPPORTED: arm
|
||||
|
||||
#include <stdio.h>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// Test that fake stack (introduced by ASan's use-after-return mode) is included
|
||||
// in the root set.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_registers=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -O2 -o %t
|
||||
// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %run %t 2>&1
|
||||
// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS="" %run %t 2>&1
|
||||
// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 %env_lsan_opts=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 %env_lsan_opts=$LSAN_BASE:"use_stacks=1" %run %t 2>&1
|
||||
// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 %env_lsan_opts="" %run %t 2>&1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Test that initialized globals are included in the root set.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=1" %run %t 2>&1
|
||||
// RUN: LSAN_OPTIONS="" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_globals=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_globals=1" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts="" %run %t 2>&1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Test that uninitialized globals are included in the root set.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=1" %run %t 2>&1
|
||||
// RUN: LSAN_OPTIONS="" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_globals=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_globals=1" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts="" %run %t 2>&1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// ASan-poisoned memory should be ignored if use_poisoned is false.
|
||||
// REQUIRES: asan
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_poisoned=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_poisoned=1" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_poisoned=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_poisoned=1" %run %t 2>&1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Test that registers of running threads are included in the root set.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_stacks=0"
|
||||
// RUN: %clangxx_lsan -pthread %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_registers=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_registers=1" %run %t 2>&1
|
||||
// RUN: LSAN_OPTIONS="" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_registers=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_registers=1" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts="" %run %t 2>&1
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Test that stack of main thread is included in the root set.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_registers=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %run %t 2>&1
|
||||
// RUN: LSAN_OPTIONS="" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_stacks=1" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts="" %run %t 2>&1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Test that stacks of non-main threads are included in the root set.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_registers=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_registers=0"
|
||||
// RUN: %clangxx_lsan -pthread %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %run %t 2>&1
|
||||
// RUN: LSAN_OPTIONS="" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_stacks=1" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts="" %run %t 2>&1
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Test that unaligned pointers are detected correctly.
|
||||
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0"
|
||||
// RUN: %clangxx_lsan %s -o %t
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_unaligned=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_unaligned=1" %run %t 2>&1
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_unaligned=0" not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_lsan_opts=$LSAN_BASE:"use_unaligned=1" %run %t 2>&1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,21 @@ else:
|
|||
lit_config.fatal("Unknown LSan test mode: %r" % lsan_lit_test_mode)
|
||||
config.name += config.name_suffix
|
||||
|
||||
# Platform-specific default LSAN_OPTIONS for lit tests.
|
||||
default_lsan_opts = 'detect_leaks=1'
|
||||
if config.host_os == 'Darwin':
|
||||
# On Darwin, we default to `abort_on_error=1`, which would make tests run
|
||||
# much slower. Let's override this and run lit tests with 'abort_on_error=0'.
|
||||
# Also, make sure we do not overwhelm the syslog while testing.
|
||||
default_lsan_opts += ':abort_on_error=0'
|
||||
default_lsan_opts += ':log_to_syslog=0'
|
||||
|
||||
if default_lsan_opts:
|
||||
config.environment['LSAN_OPTIONS'] = default_lsan_opts
|
||||
default_lsan_opts += ':'
|
||||
config.substitutions.append(('%env_lsan_opts=',
|
||||
'env LSAN_OPTIONS=' + default_lsan_opts))
|
||||
|
||||
if lit.util.which('strace'):
|
||||
config.available_features.add('strace')
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ config.available_features.add(config.tool_name)
|
|||
if config.target_arch not in ['arm', 'armhf', 'aarch64']:
|
||||
config.available_features.add('stable-runtime')
|
||||
|
||||
if config.host_os == 'Linux' and config.target_arch == 'i386' and config.tool_name == "lsan":
|
||||
if config.host_os == 'Linux' and config.tool_name == "lsan" and (config.target_arch == 'i386' or config.target_arch == 'i686'):
|
||||
config.available_features.add("lsan-x86")
|
||||
|
||||
if config.host_os == 'Darwin':
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
extern "C" {
|
||||
void __tsan_on_report(void *report);
|
||||
int __tsan_get_report_thread(void *report, unsigned long idx, int *tid,
|
||||
unsigned long *os_id, int *running,
|
||||
uint64_t *os_id, int *running,
|
||||
const char **name, int *parent_tid, void **trace,
|
||||
unsigned long trace_size);
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ void __tsan_on_report(void *report) {
|
|||
fprintf(stderr, "__tsan_on_report(%p)\n", report);
|
||||
|
||||
int tid;
|
||||
unsigned long os_id;
|
||||
uint64_t os_id;
|
||||
int running;
|
||||
const char *name;
|
||||
int parent_tid;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
#endif
|
||||
|
||||
extern "C" int __tsan_get_alloc_stack(void *addr, void **trace, size_t size,
|
||||
int *thread_id, void *os_id);
|
||||
int *thread_id, uint64_t *os_id);
|
||||
|
||||
char *mem;
|
||||
void alloc_func() { mem = (char *)malloc(10); }
|
||||
|
|
@ -49,7 +49,7 @@ int main() {
|
|||
void *trace[100];
|
||||
size_t num_frames = 100;
|
||||
int thread_id;
|
||||
void *thread_os_id;
|
||||
uint64_t *thread_os_id;
|
||||
num_frames =
|
||||
__tsan_get_alloc_stack(mem, trace, num_frames, &thread_id, &thread_os_id);
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ int main() {
|
|||
// CHECK: alloc stack retval ok
|
||||
fprintf(stderr, "thread id = %d\n", thread_id);
|
||||
// CHECK: thread id = 1
|
||||
fprintf(stderr, "thread os id = 0x%llx\n", (uint64_t)thread_os_id);
|
||||
fprintf(stderr, "thread os id = 0x%llx\n", thread_os_id);
|
||||
// CHECK: thread os id = [[THREAD_OS_ID]]
|
||||
fprintf(stderr, "%p\n", trace[0]);
|
||||
// CHECK: [[ALLOC_FRAME_0:0x[0-9a-f]+]]
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// RUN: %deflake %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -20,7 +21,7 @@ int __tsan_get_report_mop(void *report, unsigned long idx, int *tid,
|
|||
void **addr, int *size, int *write, int *atomic,
|
||||
void **trace, unsigned long trace_size);
|
||||
int __tsan_get_report_thread(void *report, unsigned long idx, int *tid,
|
||||
unsigned long *os_id, int *running,
|
||||
uint64_t *os_id, int *running,
|
||||
const char **name, int *parent_tid, void **trace,
|
||||
unsigned long trace_size);
|
||||
}
|
||||
|
|
@ -90,7 +91,7 @@ void __tsan_on_report(void *report) {
|
|||
fprintf(stderr, "thread_count = %d\n", thread_count);
|
||||
// CHECK: thread_count = 2
|
||||
|
||||
unsigned long os_id;
|
||||
uint64_t os_id;
|
||||
int running;
|
||||
const char *name;
|
||||
int parent_tid;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
// RUN: %clangxx_xray -g -std=c++11 %s -o %t
|
||||
// RUN: rm fdr-logging-test-* || true
|
||||
// RUN: rm fdr-unwrite-test-* || true
|
||||
// RUN: XRAY_OPTIONS="patch_premain=false xray_naive_log=false xray_logfile_base=fdr-logging-test- xray_fdr_log=true verbosity=1 xray_fdr_log_func_duration_threshold_us=0" %run %t 2>&1 | FileCheck %s
|
||||
// RUN: XRAY_OPTIONS="patch_premain=false xray_naive_log=false xray_logfile_base=fdr-unwrite-test- xray_fdr_log=true verbosity=1 xray_fdr_log_func_duration_threshold_us=5000" %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %llvm_xray convert --symbolize --output-format=yaml -instr_map=%t "`ls fdr-logging-test-* | head -1`" | FileCheck %s --check-prefix=TRACE
|
||||
|
|
@ -7,6 +9,7 @@
|
|||
// RUN: rm fdr-unwrite-test-*
|
||||
// FIXME: Make llvm-xray work on non-x86_64 as well.
|
||||
// REQUIRES: x86_64-linux
|
||||
// REQUIRES: built-in-llvm-tree
|
||||
|
||||
#include "xray/xray_log_interface.h"
|
||||
#include <cassert>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
// RUN: %clangxx_xray -g -std=c++11 %s -o %t
|
||||
// RUN: XRAY_OPTIONS="patch_premain=false xray_naive_log=false xray_logfile_base=fdr-thread-order. xray_fdr_log=true verbosity=1" %run %t 2>&1 | FileCheck %s
|
||||
// RUN: rm fdr-thread-order.* || true
|
||||
// RUN: XRAY_OPTIONS="patch_premain=false xray_naive_log=false xray_logfile_base=fdr-thread-order. xray_fdr_log=true verbosity=1 xray_fdr_log_func_duration_threshold_us=0" %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %llvm_xray convert --symbolize --output-format=yaml -instr_map=%t "`ls fdr-thread-order.* | head -1`" | FileCheck %s --check-prefix TRACE
|
||||
// RUN: rm fdr-thread-order.*
|
||||
// FIXME: Make llvm-xray work on non-x86_64 as well.
|
||||
// REQUIRES: x86_64-linux
|
||||
// REQUIRES: built-in-llvm-tree
|
||||
|
||||
#include "xray/xray_log_interface.h"
|
||||
#include <thread>
|
||||
#include <cassert>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,13 @@ config.name_suffix = "@XRAY_TEST_CONFIG_SUFFIX@"
|
|||
config.xray_lit_source_dir = "@XRAY_LIT_SOURCE_DIR@"
|
||||
config.target_cflags = "@XRAY_TEST_TARGET_CFLAGS@"
|
||||
config.target_arch = "@XRAY_TEST_TARGET_ARCH@"
|
||||
config.built_with_llvm = ("@COMPILER_RT_STANDALONE_BUILD@" != "TRUE")
|
||||
|
||||
# TODO: Look into whether we can run a capability test on the standalone build to
|
||||
# see whether it can run 'llvm-xray convert' instead of turning off tests for a
|
||||
# standalone build.
|
||||
if config.built_with_llvm:
|
||||
config.available_features.add('built-in-llvm-tree')
|
||||
|
||||
# Load common config for all compiler-rt lit tests
|
||||
lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
|
||||
|
|
|
|||
Loading…
Reference in a new issue