diff options
-rw-r--r-- | lib/asan/asan_activation.cc | 25 | ||||
-rw-r--r-- | lib/asan/asan_interceptors.cc | 4 | ||||
-rw-r--r-- | lib/asan/asan_rtl.cc | 15 | ||||
-rw-r--r-- | lib/lsan/lsan.cc | 5 | ||||
-rw-r--r-- | lib/msan/msan.cc | 5 | ||||
-rw-r--r-- | lib/sanitizer_common/sanitizer_common.h | 5 | ||||
-rw-r--r-- | lib/sanitizer_common/sanitizer_coverage_libcdep.cc | 94 | ||||
-rw-r--r-- | lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc | 15 | ||||
-rw-r--r-- | test/asan/TestCases/Linux/coverage-direct-activation.cc | 47 |
9 files changed, 147 insertions, 68 deletions
diff --git a/lib/asan/asan_activation.cc b/lib/asan/asan_activation.cc index 0aca1cb4e..39fa99103 100644 --- a/lib/asan/asan_activation.cc +++ b/lib/asan/asan_activation.cc @@ -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; diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc index 32c08ff56..b4925986e 100644 --- a/lib/asan/asan_interceptors.cc +++ b/lib/asan/asan_interceptors.cc @@ -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" diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc index 895ac6bf7..32ad0f208 100644 --- a/lib/asan/asan_rtl.cc +++ b/lib/asan/asan_rtl.cc @@ -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(); diff --git a/lib/lsan/lsan.cc b/lib/lsan/lsan.cc index 9a3314f38..1509b2a7d 100644 --- a/lib/lsan/lsan.cc +++ b/lib/lsan/lsan.cc @@ -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; diff --git a/lib/msan/msan.cc b/lib/msan/msan.cc index 33b856bb9..4925619ec 100644 --- a/lib/msan/msan.cc +++ b/lib/msan/msan.cc @@ -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); diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h index b3aac1efc..ebbb00324 100644 --- a/lib/sanitizer_common/sanitizer_common.h +++ b/lib/sanitizer_common/sanitizer_common.h @@ -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(); diff --git a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc index 8f0076f2c..47ddba423 100644 --- a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc @@ -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); } diff --git a/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc index 6a8cb8bf1..c0e406abd 100644 --- a/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc @@ -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)) { diff --git a/test/asan/TestCases/Linux/coverage-direct-activation.cc b/test/asan/TestCases/Linux/coverage-direct-activation.cc new file mode 100644 index 000000000..47f577570 --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-direct-activation.cc @@ -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 |