forked from OSchip/llvm-project
[asan] Allow enabling coverage at activation.
llvm-svn: 224838
This commit is contained in:
parent
fe5c5b98b3
commit
be9a53fec6
|
@ -26,6 +26,8 @@ static struct AsanDeactivatedFlags {
|
|||
AllocatorOptions allocator_options;
|
||||
int malloc_context_size;
|
||||
bool poison_heap;
|
||||
bool coverage;
|
||||
const char *coverage_dir;
|
||||
|
||||
void OverrideFromActivationFlags() {
|
||||
Flags f;
|
||||
|
@ -53,16 +55,19 @@ static struct AsanDeactivatedFlags {
|
|||
allocator_options.SetFrom(&f, &cf);
|
||||
malloc_context_size = cf.malloc_context_size;
|
||||
poison_heap = f.poison_heap;
|
||||
coverage = cf.coverage;
|
||||
coverage_dir = cf.coverage_dir;
|
||||
}
|
||||
|
||||
void Print() {
|
||||
Report("quarantine_size_mb %d, max_redzone %d, poison_heap %d, "
|
||||
"malloc_context_size %d, alloc_dealloc_mismatch %d, "
|
||||
"allocator_may_return_null %d\n",
|
||||
allocator_options.quarantine_size_mb, allocator_options.max_redzone,
|
||||
poison_heap, malloc_context_size,
|
||||
allocator_options.alloc_dealloc_mismatch,
|
||||
allocator_options.may_return_null);
|
||||
Report(
|
||||
"quarantine_size_mb %d, max_redzone %d, poison_heap %d, "
|
||||
"malloc_context_size %d, alloc_dealloc_mismatch %d, "
|
||||
"allocator_may_return_null %d, coverage %d, coverage_dir %s\n",
|
||||
allocator_options.quarantine_size_mb, allocator_options.max_redzone,
|
||||
poison_heap, malloc_context_size,
|
||||
allocator_options.alloc_dealloc_mismatch,
|
||||
allocator_options.may_return_null, coverage, coverage_dir);
|
||||
}
|
||||
} asan_deactivated_flags;
|
||||
|
||||
|
@ -76,10 +81,14 @@ void AsanDeactivate() {
|
|||
GetAllocatorOptions(&asan_deactivated_flags.allocator_options);
|
||||
asan_deactivated_flags.malloc_context_size = GetMallocContextSize();
|
||||
asan_deactivated_flags.poison_heap = CanPoisonMemory();
|
||||
asan_deactivated_flags.coverage = common_flags()->coverage;
|
||||
asan_deactivated_flags.coverage_dir = common_flags()->coverage_dir;
|
||||
|
||||
// Deactivate the runtime.
|
||||
SetCanPoisonMemory(false);
|
||||
SetMallocContextSize(1);
|
||||
ReInitializeCoverage(false, nullptr);
|
||||
|
||||
AllocatorOptions disabled = asan_deactivated_flags.allocator_options;
|
||||
disabled.quarantine_size_mb = 0;
|
||||
disabled.min_redzone = 16; // Redzone must be at least 16 bytes long.
|
||||
|
@ -99,6 +108,8 @@ void AsanActivate() {
|
|||
|
||||
SetCanPoisonMemory(asan_deactivated_flags.poison_heap);
|
||||
SetMallocContextSize(asan_deactivated_flags.malloc_context_size);
|
||||
ReInitializeCoverage(asan_deactivated_flags.coverage,
|
||||
asan_deactivated_flags.coverage_dir);
|
||||
ReInitializeAllocator(asan_deactivated_flags.allocator_options);
|
||||
|
||||
asan_is_deactivated = false;
|
||||
|
|
|
@ -169,8 +169,8 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
|
|||
} while (false)
|
||||
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
|
||||
#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
|
||||
#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res) CovUpdateMapping()
|
||||
#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CovUpdateMapping()
|
||||
#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res) CoverageUpdateMapping()
|
||||
#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CoverageUpdateMapping()
|
||||
#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited)
|
||||
#include "sanitizer_common/sanitizer_common_interceptors.inc"
|
||||
|
||||
|
|
|
@ -397,11 +397,6 @@ static void AsanInitInternal() {
|
|||
|
||||
MaybeStartBackgroudThread();
|
||||
|
||||
// Now that ASan runtime is (mostly) initialized, deactivate it if
|
||||
// necessary, so that it can be re-activated when requested.
|
||||
if (flags()->start_deactivated)
|
||||
AsanDeactivate();
|
||||
|
||||
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
|
||||
// should be set to 1 prior to initializing the threads.
|
||||
asan_inited = 1;
|
||||
|
@ -410,10 +405,12 @@ static void AsanInitInternal() {
|
|||
if (flags()->atexit)
|
||||
Atexit(asan_atexit);
|
||||
|
||||
if (common_flags()->coverage) {
|
||||
__sanitizer_cov_init();
|
||||
Atexit(__sanitizer_cov_dump);
|
||||
}
|
||||
InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
|
||||
|
||||
// Now that ASan runtime is (mostly) initialized, deactivate it if
|
||||
// necessary, so that it can be re-activated when requested.
|
||||
if (flags()->start_deactivated)
|
||||
AsanDeactivate();
|
||||
|
||||
// interceptors
|
||||
InitTlsSize();
|
||||
|
|
|
@ -53,10 +53,7 @@ extern "C" void __lsan_init() {
|
|||
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit)
|
||||
Atexit(DoLeakCheck);
|
||||
|
||||
if (common_flags()->coverage) {
|
||||
__sanitizer_cov_init();
|
||||
Atexit(__sanitizer_cov_dump);
|
||||
}
|
||||
InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
|
||||
|
||||
lsan_inited = true;
|
||||
lsan_init_is_running = false;
|
||||
|
|
|
@ -377,10 +377,7 @@ void __msan_init() {
|
|||
|
||||
Symbolizer::GetOrInit()->AddHooks(EnterSymbolizer, ExitSymbolizer);
|
||||
|
||||
if (common_flags()->coverage) {
|
||||
__sanitizer_cov_init();
|
||||
Atexit(__sanitizer_cov_dump);
|
||||
}
|
||||
InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
|
||||
|
||||
MsanTSDInit(MsanTSDDtor);
|
||||
|
||||
|
|
|
@ -214,10 +214,13 @@ void PrepareForSandboxing(__sanitizer_sandbox_arguments *args);
|
|||
void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args);
|
||||
void SetSandboxingCallback(void (*f)());
|
||||
|
||||
void CovUpdateMapping(uptr caller_pc = 0);
|
||||
void CoverageUpdateMapping();
|
||||
void CovBeforeFork();
|
||||
void CovAfterFork(int child_pid);
|
||||
|
||||
void InitializeCoverage(bool enabled, const char *coverage_dir);
|
||||
void ReInitializeCoverage(bool enabled, const char *coverage_dir);
|
||||
|
||||
void InitTlsSize();
|
||||
uptr GetTlsSize();
|
||||
|
||||
|
|
|
@ -58,12 +58,15 @@ static atomic_uintptr_t coverage_counter;
|
|||
static bool cov_sandboxed = false;
|
||||
static int cov_fd = kInvalidFd;
|
||||
static unsigned int cov_max_block_size = 0;
|
||||
static bool coverage_enabled = false;
|
||||
static const char *coverage_dir;
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
class CoverageData {
|
||||
public:
|
||||
void Init();
|
||||
void ReInit();
|
||||
void BeforeFork();
|
||||
void AfterFork(int child_pid);
|
||||
void Extend(uptr npcs);
|
||||
|
@ -130,15 +133,16 @@ class CoverageData {
|
|||
StaticSpinMutex mu;
|
||||
|
||||
void DirectOpen();
|
||||
void ReInit();
|
||||
};
|
||||
|
||||
static CoverageData coverage_data;
|
||||
|
||||
void CovUpdateMapping(const char *path, uptr caller_pc = 0);
|
||||
|
||||
void CoverageData::DirectOpen() {
|
||||
InternalScopedString path(kMaxPathLength);
|
||||
internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.raw",
|
||||
common_flags()->coverage_dir, internal_getpid());
|
||||
coverage_dir, internal_getpid());
|
||||
pc_fd = OpenFile(path.data(), true);
|
||||
if (internal_iserror(pc_fd)) {
|
||||
Report(" Coverage: failed to open %s for writing\n", path.data());
|
||||
|
@ -146,7 +150,7 @@ void CoverageData::DirectOpen() {
|
|||
}
|
||||
|
||||
pc_array_mapped_size = 0;
|
||||
CovUpdateMapping();
|
||||
CovUpdateMapping(coverage_dir);
|
||||
}
|
||||
|
||||
void CoverageData::Init() {
|
||||
|
@ -178,16 +182,22 @@ void CoverageData::Init() {
|
|||
}
|
||||
|
||||
void CoverageData::ReInit() {
|
||||
internal_munmap(pc_array, sizeof(uptr) * kPcArrayMaxSize);
|
||||
if (pc_array) {
|
||||
internal_munmap(pc_array, sizeof(uptr) * kPcArrayMaxSize);
|
||||
pc_array = nullptr;
|
||||
}
|
||||
if (pc_fd != kInvalidFd) internal_close(pc_fd);
|
||||
if (common_flags()->coverage_direct) {
|
||||
// In memory-mapped mode we must extend the new file to the known array
|
||||
// size.
|
||||
uptr size = atomic_load(&pc_array_size, memory_order_relaxed);
|
||||
Init();
|
||||
if (size) Extend(size);
|
||||
} else {
|
||||
Init();
|
||||
if (coverage_enabled) {
|
||||
if (common_flags()->coverage_direct) {
|
||||
// In memory-mapped mode we must extend the new file to the known array
|
||||
// size.
|
||||
uptr size = atomic_load(&pc_array_size, memory_order_relaxed);
|
||||
Init();
|
||||
if (size) Extend(size);
|
||||
if (coverage_enabled) CovUpdateMapping(coverage_dir);
|
||||
} else {
|
||||
Init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,13 +216,13 @@ void CoverageData::Extend(uptr npcs) {
|
|||
if (!common_flags()->coverage_direct) return;
|
||||
SpinMutexLock l(&mu);
|
||||
|
||||
if (pc_fd == kInvalidFd) DirectOpen();
|
||||
CHECK_NE(pc_fd, kInvalidFd);
|
||||
|
||||
uptr size = atomic_load(&pc_array_size, memory_order_relaxed);
|
||||
size += npcs * sizeof(uptr);
|
||||
|
||||
if (size > pc_array_mapped_size) {
|
||||
if (coverage_enabled && size > pc_array_mapped_size) {
|
||||
if (pc_fd == kInvalidFd) DirectOpen();
|
||||
CHECK_NE(pc_fd, kInvalidFd);
|
||||
|
||||
uptr new_mapped_size = pc_array_mapped_size;
|
||||
while (size > new_mapped_size) new_mapped_size += kPcArrayMmapSize;
|
||||
CHECK_LE(new_mapped_size, sizeof(uptr) * kPcArrayMaxSize);
|
||||
|
@ -361,15 +371,14 @@ static int CovOpenFile(bool packed, const char *name,
|
|||
InternalScopedString path(kMaxPathLength);
|
||||
if (!packed) {
|
||||
CHECK(name);
|
||||
path.append("%s/%s.%zd.%s", common_flags()->coverage_dir, name,
|
||||
internal_getpid(), extension);
|
||||
path.append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(),
|
||||
extension);
|
||||
} else {
|
||||
if (!name)
|
||||
path.append("%s/%zd.%s.packed", common_flags()->coverage_dir,
|
||||
internal_getpid(), extension);
|
||||
else
|
||||
path.append("%s/%s.%s.packed", common_flags()->coverage_dir, name,
|
||||
path.append("%s/%zd.%s.packed", coverage_dir, internal_getpid(),
|
||||
extension);
|
||||
else
|
||||
path.append("%s/%s.%s.packed", coverage_dir, name, extension);
|
||||
}
|
||||
uptr fd = OpenFile(path.data(), true);
|
||||
if (internal_iserror(fd)) {
|
||||
|
@ -455,7 +464,7 @@ void CoverageData::DumpCallerCalleePairs() {
|
|||
// 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.
|
||||
void CoverageData::TraceBasicBlock(uptr *cache) {
|
||||
CHECK(common_flags()->coverage);
|
||||
CHECK(coverage_enabled);
|
||||
uptr idx = *cache;
|
||||
if (!idx) {
|
||||
CHECK_LT(tr_pc_array_index, kTrPcArrayMaxSize);
|
||||
|
@ -492,7 +501,7 @@ static void CovDumpAsBitSet() {
|
|||
|
||||
// Dump the coverage on disk.
|
||||
static void CovDump() {
|
||||
if (!common_flags()->coverage || common_flags()->coverage_direct) return;
|
||||
if (!coverage_enabled || common_flags()->coverage_direct) return;
|
||||
#if !SANITIZER_WINDOWS
|
||||
if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed))
|
||||
return;
|
||||
|
@ -532,8 +541,8 @@ static void CovDump() {
|
|||
} else {
|
||||
// One file per module per process.
|
||||
path.clear();
|
||||
path.append("%s/%s.%zd.sancov", common_flags()->coverage_dir,
|
||||
module_name, internal_getpid());
|
||||
path.append("%s/%s.%zd.sancov", coverage_dir, module_name,
|
||||
internal_getpid());
|
||||
int fd = CovOpenFile(false /* packed */, module_name);
|
||||
if (fd > 0) {
|
||||
internal_write(fd, offsets.data(), offsets.size() * sizeof(u32));
|
||||
|
@ -553,7 +562,7 @@ static void CovDump() {
|
|||
|
||||
void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
|
||||
if (!args) return;
|
||||
if (!common_flags()->coverage) return;
|
||||
if (!coverage_enabled) return;
|
||||
cov_sandboxed = args->coverage_sandboxed;
|
||||
if (!cov_sandboxed) return;
|
||||
cov_fd = args->coverage_fd;
|
||||
|
@ -565,7 +574,7 @@ void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
|
|||
|
||||
int MaybeOpenCovFile(const char *name) {
|
||||
CHECK(name);
|
||||
if (!common_flags()->coverage) return -1;
|
||||
if (!coverage_enabled) return -1;
|
||||
return CovOpenFile(true /* packed */, name);
|
||||
}
|
||||
|
||||
|
@ -577,6 +586,24 @@ void CovAfterFork(int child_pid) {
|
|||
coverage_data.AfterFork(child_pid);
|
||||
}
|
||||
|
||||
void InitializeCoverage(bool enabled, const char *dir) {
|
||||
coverage_enabled = enabled;
|
||||
coverage_dir = dir;
|
||||
if (enabled) coverage_data.Init();
|
||||
if (!common_flags()->coverage_direct) Atexit(__sanitizer_cov_dump);
|
||||
}
|
||||
|
||||
void ReInitializeCoverage(bool enabled, const char *dir) {
|
||||
coverage_enabled = enabled;
|
||||
coverage_dir = dir;
|
||||
coverage_data.ReInit();
|
||||
}
|
||||
|
||||
void CoverageUpdateMapping() {
|
||||
if (coverage_enabled)
|
||||
CovUpdateMapping(coverage_dir);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
extern "C" {
|
||||
|
@ -589,19 +616,20 @@ __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_dump() { CovDump(); }
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() {
|
||||
coverage_enabled = true;
|
||||
coverage_dir = common_flags()->coverage_dir;
|
||||
coverage_data.Init();
|
||||
}
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); }
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(s32 **guards,
|
||||
uptr npcs) {
|
||||
coverage_data.InitializeGuards(guards, npcs);
|
||||
if (!common_flags()->coverage || !common_flags()->coverage_direct)
|
||||
return;
|
||||
if (SANITIZER_ANDROID) {
|
||||
if (!common_flags()->coverage_direct) return;
|
||||
if (SANITIZER_ANDROID && coverage_enabled) {
|
||||
// dlopen/dlclose interceptors do not work on Android, so we rely on
|
||||
// Extend() calls to update .sancov.map.
|
||||
CovUpdateMapping(GET_CALLER_PC());
|
||||
CovUpdateMapping(coverage_dir, GET_CALLER_PC());
|
||||
}
|
||||
coverage_data.Extend(npcs);
|
||||
}
|
||||
|
|
|
@ -62,8 +62,8 @@ struct CachedMapping {
|
|||
static CachedMapping cached_mapping;
|
||||
static StaticSpinMutex mapping_mu;
|
||||
|
||||
void CovUpdateMapping(uptr caller_pc) {
|
||||
if (!common_flags()->coverage || !common_flags()->coverage_direct) return;
|
||||
void CovUpdateMapping(const char *coverage_dir, uptr caller_pc) {
|
||||
if (!common_flags()->coverage_direct) return;
|
||||
|
||||
SpinMutexLock l(&mapping_mu);
|
||||
|
||||
|
@ -92,11 +92,10 @@ void CovUpdateMapping(uptr caller_pc) {
|
|||
}
|
||||
|
||||
int err;
|
||||
InternalScopedString tmp_path(64 +
|
||||
internal_strlen(common_flags()->coverage_dir));
|
||||
InternalScopedString tmp_path(64 + internal_strlen(coverage_dir));
|
||||
uptr res = internal_snprintf((char *)tmp_path.data(), tmp_path.size(),
|
||||
"%s/%zd.sancov.map.tmp", common_flags()->coverage_dir,
|
||||
internal_getpid());
|
||||
"%s/%zd.sancov.map.tmp", coverage_dir,
|
||||
internal_getpid());
|
||||
CHECK_LE(res, tmp_path.size());
|
||||
uptr map_fd = OpenFile(tmp_path.data(), true);
|
||||
if (internal_iserror(map_fd, &err)) {
|
||||
|
@ -112,9 +111,9 @@ void CovUpdateMapping(uptr caller_pc) {
|
|||
}
|
||||
internal_close(map_fd);
|
||||
|
||||
InternalScopedString path(64 + internal_strlen(common_flags()->coverage_dir));
|
||||
InternalScopedString path(64 + internal_strlen(coverage_dir));
|
||||
res = internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.map",
|
||||
common_flags()->coverage_dir, internal_getpid());
|
||||
coverage_dir, internal_getpid());
|
||||
CHECK_LE(res, path.size());
|
||||
res = internal_rename(tmp_path.data(), path.data());
|
||||
if (internal_iserror(res, &err)) {
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// Test for direct coverage writing enabled as activation time.
|
||||
|
||||
// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_direct_test_1.so -fPIC
|
||||
// RUN: %clangxx -c -DSO_DIR=\"%T\" %s -o %t.o
|
||||
// RUN: %clangxx_asan -fsanitize-coverage=1 %t.o %libdl -o %t
|
||||
|
||||
// RUN: rm -rf %T/coverage-direct-activation
|
||||
|
||||
// RUN: mkdir -p %T/coverage-direct-activation/normal
|
||||
// RUN: ASAN_OPTIONS=coverage=1,coverage_direct=0,coverage_dir=%T/coverage-direct-activation/normal:verbosity=1 %run %t
|
||||
// RUN: %sancov print %T/coverage-direct-activation/normal/*.sancov >%T/coverage-direct-activation/normal/out.txt
|
||||
|
||||
// RUN: mkdir -p %T/coverage-direct-activation/direct
|
||||
// RUN: ASAN_OPTIONS=start_deactivated=1,coverage_direct=1,verbosity=1 \
|
||||
// RUN: ASAN_ACTIVATION_OPTIONS=coverage=1,coverage_dir=%T/coverage-direct-activation/direct %run %t
|
||||
// RUN: cd %T/coverage-direct-activation/direct
|
||||
// RUN: %sancov rawunpack *.sancov.raw
|
||||
// RUN: %sancov print *.sancov >out.txt
|
||||
// RUN: cd ../..
|
||||
|
||||
// RUN: diff -u coverage-direct-activation/normal/out.txt coverage-direct-activation/direct/out.txt
|
||||
|
||||
// XFAIL: android
|
||||
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef SHARED
|
||||
extern "C" {
|
||||
void bar() { printf("bar\n"); }
|
||||
}
|
||||
#else
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
fprintf(stderr, "PID: %d\n", getpid());
|
||||
void *handle1 =
|
||||
dlopen(SO_DIR "/libcoverage_direct_test_1.so", RTLD_LAZY);
|
||||
assert(handle1);
|
||||
void (*bar1)() = (void (*)())dlsym(handle1, "bar");
|
||||
assert(bar1);
|
||||
bar1();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue