Index: include/sanitizer/coverage_interface.h =================================================================== --- include/sanitizer/coverage_interface.h +++ include/sanitizer/coverage_interface.h @@ -23,6 +23,11 @@ void __sanitizer_cov_init(); // Record and dump coverage info. void __sanitizer_cov_dump(); + + // Dump collected coverage info. Sorts pcs by module into individual + // .sancov files. + void __sanitizer_dump_coverage(const uintptr_t *pcs, uintptr_t len); + // Open .sancov.packed in the coverage directory and return the file // descriptor. Returns -1 on failure, or if coverage dumping is disabled. // This is intended for use by sandboxing code. Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -53,6 +53,7 @@ set(SANITIZER_LIBCDEP_SOURCES sanitizer_common_libcdep.cc sanitizer_coverage_libcdep.cc + sanitizer_coverage_libcdep_new.cc sanitizer_coverage_mapping_libcdep.cc sanitizer_linux_libcdep.cc sanitizer_posix_libcdep.cc Index: lib/sanitizer_common/sanitizer_common.h =================================================================== --- lib/sanitizer_common/sanitizer_common.h +++ lib/sanitizer_common/sanitizer_common.h @@ -547,6 +547,13 @@ uptr capacity() const { return capacity_; } + void resize(uptr new_size) { + Resize(new_size); + if (new_size > size_) { + internal_memset(&data_[size_], 0, sizeof(T) * (new_size - size_)); + } + size_ = new_size; + } void clear() { size_ = 0; } bool empty() const { return size() == 0; } Index: lib/sanitizer_common/sanitizer_coverage_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_coverage_libcdep.cc +++ lib/sanitizer_common/sanitizer_coverage_libcdep.cc @@ -954,6 +954,7 @@ } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { coverage_data.DumpAll(); + __sanitizer_dump_trace_pc_guard_coverage(); } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(s32 *guards, uptr npcs, u8 *counters, @@ -1036,10 +1037,6 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_cov_trace_gep() {} SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE -void __sanitizer_cov_trace_pc_guard() {} -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_cov_trace_pc_indir() {} -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE -void __sanitizer_cov_trace_pc_guard_init() {} #endif // !SANITIZER_WINDOWS } // extern "C" Index: lib/sanitizer_common/sanitizer_coverage_libcdep_new.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_coverage_libcdep_new.cc @@ -0,0 +1,165 @@ +//===-- sanitizer_coverage_libcdep_new.cc ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Sanitizer Coverage Controller for Trace PC Guard. + +#include "sanitizer_allocator_internal.h" +#include "sanitizer_atomic.h" +#include "sanitizer_common.h" +#include "sanitizer_symbolizer.h" + +using namespace __sanitizer; + +using AddressRange = LoadedModule::AddressRange; + +namespace { + +static const u64 Magic64 = 0xC0BFFFFFFFFFFF64ULL; +static const u64 Magic32 = 0xC0BFFFFFFFFFFF32ULL; +static const u64 Magic = SANITIZER_WORDSIZE == 64 ? Magic64 : Magic32; + +static fd_t OpenFile(const char* path) { + error_t err; + fd_t fd = OpenFile(path, WrOnly, &err); + if (fd == kInvalidFd) + Report("SanitizerCoverage: failed to open %s for writing (reason: %d)\n", + path, err); + return fd; +} + +static void GetCoverageFilename(char* path, const char* name, + const char* extension) { + CHECK(name); + internal_snprintf(path, kMaxPathLength, "%s/%s.%zd.%s", + common_flags()->coverage_dir, name, internal_getpid(), + extension); +} + +static void WriteModuleCoverage(char* file_path, const char* module_name, + const uptr* pcs, uptr len) { + GetCoverageFilename(file_path, StripModuleName(module_name), "sancov"); + fd_t fd = OpenFile(file_path); + WriteToFile(fd, &Magic, sizeof(Magic)); + WriteToFile(fd, pcs, len * sizeof(*pcs)); + CloseFile(fd); + Printf("SanitizerCoverage: %s %zd PCs written\n", file_path, len); +} + +static void SanitizerDumpCoverage(const uptr* unsorted_pcs, uptr len) { + if (!len) return; + + char* file_path = static_cast(InternalAlloc(kMaxPathLength)); + char* module_name = static_cast(InternalAlloc(kMaxPathLength)); + uptr* pcs = static_cast(InternalAlloc(len * sizeof(uptr))); + + internal_memcpy(pcs, unsorted_pcs, len * sizeof(uptr)); + SortArray(pcs, len); + + bool module_found = false; + uptr last_base = 0; + uptr module_start_idx = 0; + + for (uptr i = 0; i < len; ++i) { + const uptr pc = pcs[i]; + if (!pc) continue; + + if (!__sanitizer_get_module_and_offset_for_pc(pc, nullptr, 0, &pcs[i])) { + Printf("ERROR: bad pc %x\n", pc); + continue; + } + uptr module_base = pc - pcs[i]; + + if (module_base != last_base || !module_found) { + if (module_found) { + WriteModuleCoverage(file_path, module_name, &pcs[module_start_idx], + i - module_start_idx); + } + + last_base = module_base; + module_start_idx = i; + module_found = true; + __sanitizer_get_module_and_offset_for_pc(pc, module_name, kMaxPathLength, + &pcs[i]); + } + } + + if (module_found) { + WriteModuleCoverage(file_path, module_name, &pcs[module_start_idx], + len - module_start_idx); + } + + InternalFree(file_path); + InternalFree(module_name); + InternalFree(pcs); +} + +// Collects trace-pc guard coverage. +// This class relies on zero-initialization. +class TracePcGuardController { + public: + void Initialize() { + CHECK(!initialized); + + initialized = true; + pc_vector.Initialize(0); + } + + void InitTracePcGuard(u32* start, u32* end) { + if (!initialized) Initialize(); + CHECK(!*start); + CHECK_NE(start, end); + + u32 i = pc_vector.size(); + for (u32* p = start; p < end; p++) *p = ++i; + pc_vector.resize(i); + } + + void TracePcGuard(u32* guard, uptr pc) { + atomic_uint32_t* guard_ptr = reinterpret_cast(guard); + u32 idx = atomic_exchange(guard_ptr, 0, memory_order_relaxed); + if (!idx) return; + // we start indices from 1. + pc_vector[idx - 1] = pc; + } + + void Dump() { + if (!initialized) return; + __sanitizer_dump_coverage(pc_vector.data(), pc_vector.size()); + } + + private: + bool initialized; + InternalMmapVectorNoCtor pc_vector; +}; + +static TracePcGuardController pc_guard_controller; + +}; // namespace + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage( // NOLINT + const uptr* pcs, uptr len) { + return SanitizerDumpCoverage(pcs, len); +} + +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void +__sanitizer_cov_trace_pc_guard(u32* guard) { + if (!*guard) return; + pc_guard_controller.TracePcGuard(guard, GET_CALLER_PC() - 1); +} + +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void +__sanitizer_cov_trace_pc_guard_init(u32* start, u32* end) { + if (start == end || *start) return; + pc_guard_controller.InitTracePcGuard(start, end); +} + +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_trace_pc_guard_coverage() { + pc_guard_controller.Dump(); +} +} // extern "C" Index: lib/sanitizer_common/sanitizer_interface_internal.h =================================================================== --- lib/sanitizer_common/sanitizer_interface_internal.h +++ lib/sanitizer_common/sanitizer_interface_internal.h @@ -46,8 +46,12 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_report_error_summary(const char *error_summary); - SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump(); SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init(); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump(); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage( + const __sanitizer::uptr *pcs, const __sanitizer::uptr len); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_trace_pc_guard_coverage(); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard); SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_annotate_contiguous_container(const void *beg, @@ -60,6 +64,11 @@ SANITIZER_INTERFACE_ATTRIBUTE const void *__sanitizer_contiguous_container_find_bad_address( const void *beg, const void *mid, const void *end); + + SANITIZER_INTERFACE_ATTRIBUTE + int __sanitizer_get_module_and_offset_for_pc( + __sanitizer::uptr pc, char *module_path, + __sanitizer::uptr module_path_len, __sanitizer::uptr *pc_offset); } // extern "C" #endif // SANITIZER_INTERFACE_INTERNAL_H Index: test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard-dso.cc =================================================================== --- /dev/null +++ test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard-dso.cc @@ -0,0 +1,72 @@ +// Tests trace pc guard coverage collection. +// +// REQUIRES: has_sancovcc +// XFAIL: tsan +// +// RUN: DIR=%t_workdir +// RUN: CLANG_ARGS="-O0 -fsanitize-coverage=trace-pc-guard" +// RUN: rm -rf $DIR +// RUN: mkdir -p $DIR +// RUN: cd $DIR +// RUN: %clangxx -DSHARED1 $CLANG_ARGS -shared %s -o %t_1.so -fPIC +// RUN: %clangxx -DSHARED2 $CLANG_ARGS -shared %s -o %t_2.so -fPIC +// RUN: %clangxx -DMAIN $CLANG_ARGS %s -o %t %t_1.so %t_2.so +// RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s +// RUN: %sancovcc -covered-functions -strip_path_prefix=TestCases/ *.sancov \ +// RUN: %t %t_1.so %t_2.so 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-SANCOV %s +// RUN: rm -rf $DIR + +#include + +extern "C" { + int bar(); + int baz(); +} + +#ifdef MAIN + +int foo() { + fprintf(stderr, "foo\n"); + return 1; +} + +int main() { + fprintf(stderr, "main\n"); + foo(); + bar(); + baz(); +} + +#endif // MAIN + +extern "C" { + +#ifdef SHARED1 +int bar() { + fprintf(stderr, "bar\n"); + return 1; +} +#endif + +#ifdef SHARED2 +int baz() { + fprintf(stderr, "baz\n"); + return 1; +} +#endif + +} // extern "C" + +// CHECK: main +// CHECK-NEXT: foo +// CHECK-NEXT: bar +// CHECK-NEXT: baz +// CHECK-NEXT: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard-dso.{{.*}}.sancov 2 PCs written +// CHECK-NEXT: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard-dso.{{.*}}_2.so.{{.*}}.sancov 1 PCs written +// CHECK-NEXT: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard-dso.{{.*}}_1.so.{{.*}}.sancov 1 PCs written +// +// CHECK-SANCOV: Ignoring {{.*}}_1.so and its coverage because __sanitizer_cov* functions were not found. +// CHECK-SANCOV: Ignoring {{.*}}_2.so and its coverage because __sanitizer_cov* functions were not found. +// CHECK-SANCOV-NEXT: sanitizer_coverage_trace_pc_guard-dso.cc:29 foo +// CHECK-SANCOV-NEXT: sanitizer_coverage_trace_pc_guard-dso.cc:34 main Index: test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc =================================================================== --- /dev/null +++ test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc @@ -0,0 +1,35 @@ +// Tests trace pc guard coverage collection. +// +// REQUIRES: has_sancovcc +// XFAIL: tsan +// +// RUN: DIR=%t_workdir +// RUN: rm -rf $DIR +// RUN: mkdir -p $DIR +// RUN: cd $DIR +// RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -ldl -o %t +// RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s +// RUN: %sancovcc -covered-functions -strip_path_prefix=TestCases/ *.sancov %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-SANCOV %s +// RUN: rm -rf $DIR + +#include + +int foo() { + fprintf(stderr, "foo\n"); + return 1; +} + +int main() { + fprintf(stderr, "main\n"); + foo(); + foo(); +} + +// CHECK: main +// CHECK-NEXT: foo +// CHECK-NEXT: foo +// CHECK-NEXT: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard.{{.*}}.sancov 2 PCs written +// +// CHECK-SANCOV: sanitizer_coverage_trace_pc_guard.cc:18 foo +// CHECK-SANCOV-NEXT: sanitizer_coverage_trace_pc_guard.cc:23 main