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_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,160 @@ +//===-- 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/coverage_interface.h" +#include "sanitizer_common.h" +#include "sanitizer_symbolizer.h" +#include "sanitizer_allocator_internal.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 CloseSancovFile(fd_t fd, const char* file_path, uptr num_offsets) { + if (fd == kInvalidFd) return; + CloseFile(fd); + Printf("SanitizerCoverage: %s %zd PCs written\n", file_path, num_offsets); +} + +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 SanitizerDumpCoverage(const uptr* unsorted_pcs, uptr len) { + 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); + + + uptr last_base = 0; + uptr num_offsets = 0; + fd_t fd = kInvalidFd; + + for (uptr i = 0; i < len; ++i) { + const uptr pc = pcs[i]; + uptr offset; + if (!__sanitizer_get_module_and_offset_for_pc(pc, nullptr, 0, &offset)) { + Printf("ERROR: bad pc %x\n", pc); + continue; + } + uptr module_base = pc - offset; + + if (fd == kInvalidFd || module_base != last_base) { + CloseSancovFile(fd, file_path, num_offsets); + num_offsets = 0; + last_base = module_base; + __sanitizer_get_module_and_offset_for_pc(pc, module_name, kMaxPathLength, + &offset); + GetCoverageFilename(file_path, StripModuleName(module_name), "sancov"); + fd = OpenFile(file_path); + WriteToFile(fd, &Magic, sizeof(Magic)); + } + + num_offsets++; + WriteToFile(fd, &offset, sizeof(offset)); + } + + CloseSancovFile(fd, file_path, num_offsets); + InternalFree(file_path); + InternalFree(module_name); + InternalFree(pcs); +} + +class TracePcGuardController { + public: + void InitTracePcGuard(u32* start, u32* end) { + CHECK(!enabled); + CHECK(!pc_array); + CHECK_NE(start, end); + CHECK(!*start); + + enabled = true; + pc_array = reinterpret_cast( + MmapNoReserveOrDie(sizeof(uptr) * kPcArrayMaxSize, "CovInit")); + atomic_store(&pc_array_index, 0, memory_order_relaxed); + + for (u32* p = start; p < end; p++) *p = 1; + } + + void TracePcGuard(u32* guard, uptr pc) { + *guard = 0; + uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed); + pc_array[idx] = pc; + } + + void Dump() { + if (!enabled) return; + uptr size = atomic_load(&pc_array_index, memory_order_relaxed); + __sanitizer_dump_coverage(pc_array, size); + } + + private: + // Maximal size pc array may ever grow. + // We MmapNoReserve this space to ensure that the array is contiguous. + static const uptr kPcArrayMaxSize = + FIRST_32_SECOND_64(1 << (SANITIZER_ANDROID ? 24 : 26), 1 << 27); + + bool enabled; + uptr* pc_array; + + // Index of the first available pc_array slot. + atomic_uintptr_t pc_array_index; +}; + +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.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/sanitizer_coverage_trace_pc_guard +// 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