diff --git a/compiler-rt/lib/dfsan/CMakeLists.txt b/compiler-rt/lib/dfsan/CMakeLists.txt --- a/compiler-rt/lib/dfsan/CMakeLists.txt +++ b/compiler-rt/lib/dfsan/CMakeLists.txt @@ -7,6 +7,7 @@ dfsan_chained_origin_depot.cpp dfsan_custom.cpp dfsan_interceptors.cpp + dfsan_new_delete.cpp dfsan_thread.cpp ) diff --git a/compiler-rt/lib/dfsan/dfsan.h b/compiler-rt/lib/dfsan/dfsan.h --- a/compiler-rt/lib/dfsan/dfsan.h +++ b/compiler-rt/lib/dfsan/dfsan.h @@ -65,7 +65,7 @@ extern bool dfsan_inited; extern bool dfsan_init_is_running; -void InitializeInterceptors(); +void initialize_interceptors(); inline dfsan_label *shadow_for(void *ptr) { return (dfsan_label *) ((((uptr) ptr) & ShadowMask()) << 1); diff --git a/compiler-rt/lib/dfsan/dfsan.cpp b/compiler-rt/lib/dfsan/dfsan.cpp --- a/compiler-rt/lib/dfsan/dfsan.cpp +++ b/compiler-rt/lib/dfsan/dfsan.cpp @@ -21,7 +21,6 @@ #include "dfsan/dfsan.h" #include "dfsan/dfsan_chained_origin_depot.h" -#include "dfsan/dfsan_flags.h" #include "dfsan/dfsan_origin.h" #include "dfsan/dfsan_thread.h" #include "sanitizer_common/sanitizer_atomic.h" @@ -1039,7 +1038,7 @@ if (!(init_addr >= UnusedAddr() && init_addr < AppAddr())) MmapFixedNoAccess(UnusedAddr(), AppAddr() - UnusedAddr()); - InitializeInterceptors(); + initialize_interceptors(); // Register the fini callback to run when the program terminates successfully // or it is killed by the runtime. diff --git a/compiler-rt/lib/dfsan/dfsan_custom.cpp b/compiler-rt/lib/dfsan/dfsan_custom.cpp --- a/compiler-rt/lib/dfsan/dfsan_custom.cpp +++ b/compiler-rt/lib/dfsan/dfsan_custom.cpp @@ -461,24 +461,6 @@ return r; } -SANITIZER_INTERFACE_ATTRIBUTE void *__dfsw_calloc(size_t nmemb, size_t size, - dfsan_label nmemb_label, - dfsan_label size_label, - dfsan_label *ret_label) { - void *p = calloc(nmemb, size); - dfsan_set_label(0, p, nmemb * size); - *ret_label = 0; - return p; -} - -SANITIZER_INTERFACE_ATTRIBUTE void *__dfso_calloc( - size_t nmemb, size_t size, dfsan_label nmemb_label, dfsan_label size_label, - dfsan_label *ret_label, dfsan_origin nmemb_origin, dfsan_origin size_origin, - dfsan_origin *ret_origin) { - void *p = __dfsw_calloc(nmemb, size, nmemb_label, size_label, ret_label); - *ret_origin = 0; - return p; -} SANITIZER_INTERFACE_ATTRIBUTE size_t __dfsw_strlen(const char *s, dfsan_label s_label, dfsan_label *ret_label) { diff --git a/compiler-rt/lib/dfsan/dfsan_interceptors.cpp b/compiler-rt/lib/dfsan/dfsan_interceptors.cpp --- a/compiler-rt/lib/dfsan/dfsan_interceptors.cpp +++ b/compiler-rt/lib/dfsan/dfsan_interceptors.cpp @@ -15,8 +15,14 @@ #include #include "dfsan/dfsan.h" +#include "dfsan/dfsan_thread.h" #include "interception/interception.h" +#include "sanitizer_common/sanitizer_allocator_interface.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_errno.h" +#include "sanitizer_common/sanitizer_platform_limits_posix.h" +#include "sanitizer_common/sanitizer_posix.h" +#include "sanitizer_common/sanitizer_tls_get_addr.h" using namespace __sanitizer; @@ -26,44 +32,224 @@ } // namespace -INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags, - int fd, OFF_T offset) { - void *res; +INTERCEPTOR(void *, reallocarray, void *ptr, SIZE_T nmemb, SIZE_T size) { + return __dfsan::dfsan_reallocarray(ptr, nmemb, size); +} + +INTERCEPTOR(void *, __libc_memalign, SIZE_T alignment, SIZE_T size) { + void *ptr = __dfsan::dfsan_memalign(alignment, size); + if (ptr) + DTLS_on_libc_memalign(ptr, size); + return ptr; +} + +INTERCEPTOR(void *, aligned_alloc, SIZE_T alignment, SIZE_T size) { + return __dfsan::dfsan_aligned_alloc(alignment, size); +} + +static uptr allocated_for_dlsym; +static const uptr kDlsymAllocPoolSize = 1024; +static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize]; + +static bool IsInDlsymAllocPool(const void *ptr) { + uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym; + return off < sizeof(alloc_memory_for_dlsym); +} + +static void *AllocateFromLocalPool(uptr size_in_bytes) { + uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize; + void *mem = (void *)&alloc_memory_for_dlsym[allocated_for_dlsym]; + allocated_for_dlsym += size_in_words; + CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize); + return mem; +} + +INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) { + if (UNLIKELY(!__dfsan::dfsan_inited)) + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. + return AllocateFromLocalPool(nmemb * size); + return __dfsan::dfsan_calloc(nmemb, size); +} + +INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) { + if (UNLIKELY(IsInDlsymAllocPool(ptr))) { + uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym; + uptr copy_size = Min(size, kDlsymAllocPoolSize - offset); + void *new_ptr; + if (UNLIKELY(!__dfsan::dfsan_inited)) { + new_ptr = AllocateFromLocalPool(copy_size); + } else { + copy_size = size; + new_ptr = __dfsan::dfsan_malloc(copy_size); + } + internal_memcpy(new_ptr, ptr, copy_size); + return new_ptr; + } + return __dfsan::dfsan_realloc(ptr, size); +} + +INTERCEPTOR(void *, malloc, SIZE_T size) { + if (UNLIKELY(!__dfsan::dfsan_inited)) + // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym. + return AllocateFromLocalPool(size); + return __dfsan::dfsan_malloc(size); +} + +INTERCEPTOR(void, free, void *ptr) { + if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) + return; + return __dfsan::dfsan_deallocate(ptr); +} + +INTERCEPTOR(void, cfree, void *ptr) { + if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) + return; + return __dfsan::dfsan_deallocate(ptr); +} + +INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) { + CHECK_NE(memptr, 0); + int res = __dfsan::dfsan_posix_memalign(memptr, alignment, size); + if (!res) + dfsan_set_label(0, memptr, sizeof(*memptr)); + return res; +} + +INTERCEPTOR(void *, memalign, SIZE_T alignment, SIZE_T size) { + return __dfsan::dfsan_memalign(alignment, size); +} + +INTERCEPTOR(void *, valloc, SIZE_T size) { return __dfsan::dfsan_valloc(size); } + +INTERCEPTOR(void *, pvalloc, SIZE_T size) { + return __dfsan::dfsan_pvalloc(size); +} + +INTERCEPTOR(void, mallinfo, __sanitizer_struct_mallinfo *sret) { + internal_memset(sret, 0, sizeof(*sret)); + dfsan_set_label(0, sret, sizeof(*sret)); +} - // interceptors_initialized is set to true during preinit_array, when we're - // single-threaded. So we don't need to worry about accessing it atomically. - if (!interceptors_initialized) - res = (void *)syscall(__NR_mmap, addr, length, prot, flags, fd, offset); - else - res = REAL(mmap)(addr, length, prot, flags, fd, offset); +INTERCEPTOR(int, mallopt, int cmd, int value) { return 0; } - if (res != (void *)-1) +INTERCEPTOR(void, malloc_stats, void) { + // FIXME: implement, but don't call REAL(malloc_stats)! +} + +INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { + return __sanitizer_get_allocated_size(ptr); +} + +#define ENSURE_DFSAN_INITED() \ + do { \ + CHECK(!__dfsan::dfsan_init_is_running); \ + if (!__dfsan::dfsan_inited) { \ + __dfsan::dfsan_init(); \ + } \ + } while (0) + +struct DFSanInterceptorContext {}; + +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + if (__dfsan::dfsan_init_is_running) \ + return REAL(func)(__VA_ARGS__); \ + ENSURE_DFSAN_INITED(); \ + DFSanInterceptorContext dfsan_ctx = {}; \ + ctx = (void *)&dfsan_ctx; \ + (void)ctx; \ + dfsan_set_label(0, __errno_location(), sizeof(int)); /* NOLINT */ + +#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!__dfsan::dfsan_inited) + +INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags, + int fd, OFF_T offset) { + void *ctx; + if (common_flags()->detect_write_exec) + ReportMmapWriteExec(prot); + if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) + return (void *)internal_mmap(addr, length, prot, flags, fd, offset); + COMMON_INTERCEPTOR_ENTER(ctx, mmap, addr, length, prot, flags, fd, offset); + void *res = REAL(mmap)(addr, length, prot, flags, fd, offset); + if (res != (void *)-1) { dfsan_set_label(0, res, RoundUpTo(length, GetPageSizeCached())); + } return res; } INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags, int fd, OFF64_T offset) { + void *ctx; + if (common_flags()->detect_write_exec) + ReportMmapWriteExec(prot); + if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) + return (void *)internal_mmap(addr, length, prot, flags, fd, offset); + COMMON_INTERCEPTOR_ENTER(ctx, mmap64, addr, length, prot, flags, fd, offset); void *res = REAL(mmap64)(addr, length, prot, flags, fd, offset); - if (res != (void *)-1) + if (res != (void *)-1) { dfsan_set_label(0, res, RoundUpTo(length, GetPageSizeCached())); + } return res; } INTERCEPTOR(int, munmap, void *addr, SIZE_T length) { + void *ctx; + if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) + return internal_munmap(addr, length); + COMMON_INTERCEPTOR_ENTER(ctx, munmap, addr, length); int res = REAL(munmap)(addr, length); if (res != -1) dfsan_set_label(0, addr, RoundUpTo(length, GetPageSizeCached())); return res; } +#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \ + if (__dfsan::DFsanThread *t = __dfsan::GetCurrentThread()) { \ + *begin = t->tls_begin(); \ + *end = t->tls_end(); \ + } else { \ + *begin = *end = 0; \ + } +#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ptr, size) \ + dfsan_set_label(0, ptr, size) + +INTERCEPTOR(void *, __tls_get_addr, void *arg) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr, arg); + void *res = REAL(__tls_get_addr)(arg); + uptr tls_begin, tls_end; + COMMON_INTERCEPTOR_GET_TLS_RANGE(&tls_begin, &tls_end); + DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res, tls_begin, tls_end); + if (dtv) { + // New DTLS block has been allocated. + COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size); + } + return res; +} + namespace __dfsan { -void InitializeInterceptors() { +void initialize_interceptors() { CHECK(!interceptors_initialized); + INTERCEPT_FUNCTION(aligned_alloc); + INTERCEPT_FUNCTION(calloc); + INTERCEPT_FUNCTION(cfree); + INTERCEPT_FUNCTION(free); + INTERCEPT_FUNCTION(mallinfo); + INTERCEPT_FUNCTION(malloc); + INTERCEPT_FUNCTION(malloc_stats); + INTERCEPT_FUNCTION(malloc_usable_size); + INTERCEPT_FUNCTION(mallopt); + INTERCEPT_FUNCTION(memalign); INTERCEPT_FUNCTION(mmap); INTERCEPT_FUNCTION(mmap64); INTERCEPT_FUNCTION(munmap); + INTERCEPT_FUNCTION(posix_memalign); + INTERCEPT_FUNCTION(pvalloc); + INTERCEPT_FUNCTION(realloc); + INTERCEPT_FUNCTION(reallocarray); + INTERCEPT_FUNCTION(valloc); + INTERCEPT_FUNCTION(__tls_get_addr); + INTERCEPT_FUNCTION(__libc_memalign); interceptors_initialized = true; } diff --git a/compiler-rt/lib/dfsan/dfsan_new_delete.cpp b/compiler-rt/lib/dfsan/dfsan_new_delete.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/dfsan/dfsan_new_delete.cpp @@ -0,0 +1,125 @@ +//===-- dfsan_new_delete.cpp +//-----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of DataflowSanitizer. +// +// Interceptors for operators new and delete. +//===----------------------------------------------------------------------===// + +#include + +#include "dfsan.h" +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_report.h" + +using namespace __dfsan; + +// Fake std::nothrow_t and std::align_val_t to avoid including . +namespace std { +struct nothrow_t {}; +enum class align_val_t : size_t {}; +} // namespace std + +// TODO(alekseys): throw std::bad_alloc instead of dying on OOM. +#define OPERATOR_NEW_BODY(nothrow) \ + void *res = dfsan_malloc(size); \ + if (!nothrow && UNLIKELY(!res)) { \ + BufferedStackTrace stack; \ + ReportOutOfMemory(size, &stack); \ + } \ + return res +#define OPERATOR_NEW_BODY_ALIGN(nothrow) \ + void *res = dfsan_memalign((uptr)align, size); \ + if (!nothrow && UNLIKELY(!res)) { \ + BufferedStackTrace stack; \ + ReportOutOfMemory(size, &stack); \ + } \ + return res; + +INTERCEPTOR_ATTRIBUTE +void *operator new(size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); } +INTERCEPTOR_ATTRIBUTE +void *operator new[](size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); } +INTERCEPTOR_ATTRIBUTE +void *operator new(size_t size, std::nothrow_t const &) { + OPERATOR_NEW_BODY(true /*nothrow*/); +} +INTERCEPTOR_ATTRIBUTE +void *operator new[](size_t size, std::nothrow_t const &) { + OPERATOR_NEW_BODY(true /*nothrow*/); +} +INTERCEPTOR_ATTRIBUTE +void *operator new(size_t size, std::align_val_t align) { + OPERATOR_NEW_BODY_ALIGN(false /*nothrow*/); +} +INTERCEPTOR_ATTRIBUTE +void *operator new[](size_t size, std::align_val_t align) { + OPERATOR_NEW_BODY_ALIGN(false /*nothrow*/); +} +INTERCEPTOR_ATTRIBUTE +void *operator new(size_t size, std::align_val_t align, + std::nothrow_t const &) { + OPERATOR_NEW_BODY_ALIGN(true /*nothrow*/); +} +INTERCEPTOR_ATTRIBUTE +void *operator new[](size_t size, std::align_val_t align, + std::nothrow_t const &) { + OPERATOR_NEW_BODY_ALIGN(true /*nothrow*/); +} + +#define OPERATOR_DELETE_BODY \ + if (ptr) \ + dfsan_deallocate(ptr) + +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr)NOEXCEPT { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr, std::nothrow_t const &) { + OPERATOR_DELETE_BODY; +} +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr, std::nothrow_t const &) { + OPERATOR_DELETE_BODY; +} +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr, size_t size)NOEXCEPT { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr, size_t size) NOEXCEPT { + OPERATOR_DELETE_BODY; +} +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr, std::align_val_t align)NOEXCEPT { + OPERATOR_DELETE_BODY; +} +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr, std::align_val_t align) NOEXCEPT { + OPERATOR_DELETE_BODY; +} +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr, std::align_val_t align, + std::nothrow_t const &) { + OPERATOR_DELETE_BODY; +} +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr, std::align_val_t align, + std::nothrow_t const &) { + OPERATOR_DELETE_BODY; +} +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr, size_t size, std::align_val_t align)NOEXCEPT { + OPERATOR_DELETE_BODY; +} +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr, size_t size, + std::align_val_t align) NOEXCEPT { + OPERATOR_DELETE_BODY; +} diff --git a/compiler-rt/lib/dfsan/done_abilist.txt b/compiler-rt/lib/dfsan/done_abilist.txt --- a/compiler-rt/lib/dfsan/done_abilist.txt +++ b/compiler-rt/lib/dfsan/done_abilist.txt @@ -39,8 +39,23 @@ ############################################################################### # glibc ############################################################################### +# Functions of memory allocators +fun:__libc_memalign=discard +fun:aligned_alloc=discard +fun:calloc=discard +fun:cfree=discard +fun:mallinfo=discard fun:malloc=discard fun:free=discard +fun:malloc_stats=discard +fun:malloc_usable_size=discard +fun:mallopt=discard +fun:memalign=discard +fun:posix_memalign=discard +fun:pvalloc=discard +fun:realloc=discard +fun:reallocarray=discard +fun:valloc=discard # Functions that return a value that depends on the input, but the output might # not be necessarily data-dependent on the input. @@ -155,7 +170,6 @@ fun:openat=discard fun:pipe=discard fun:posix_fadvise=discard -fun:posix_memalign=discard fun:prctl=discard fun:printf=discard fun:pthread_sigmask=discard @@ -191,7 +205,6 @@ # Functions that produce output does not depend on the input (need to zero the # shadow manually). fun:_dl_get_tls_static_info=custom -fun:calloc=custom fun:clock_gettime=custom fun:dlopen=custom fun:epoll_wait=custom @@ -399,6 +412,36 @@ fun:__sanitizer_cov_pcs_init=uninstrumented fun:__sanitizer_cov_pcs_init=discard +fun:__sanitizer_get_current_allocated_bytes=uninstrumented +fun:__sanitizer_get_current_allocated_bytes=discard +fun:__sanitizer_get_heap_size=uninstrumented +fun:__sanitizer_get_heap_size=discard +fun:__sanitizer_get_free_bytes=uninstrumented +fun:__sanitizer_get_free_bytes=discard +fun:__sanitizer_get_unmapped_bytes=uninstrumented +fun:__sanitizer_get_unmapped_bytes=discard +fun:__sanitizer_get_estimated_allocated_size=uninstrumented +fun:__sanitizer_get_estimated_allocated_size=discard +fun:__sanitizer_get_ownership=uninstrumented +fun:__sanitizer_get_ownership=discard +fun:__sanitizer_get_allocated_size=uninstrumented +fun:__sanitizer_get_allocated_size=discard +fun:__sanitizer_print_stack_trace=uninstrumented +fun:__sanitizer_print_stack_trace=discard + +fun:TcmallocSlab_Internal_PushBatch_FixedShift=uninstrumented +fun:TcmallocSlab_Internal_PushBatch_FixedShift=discard +fun:TcmallocSlab_Internal_PushBatch_FixedShift_VCPU=uninstrumented +fun:TcmallocSlab_Internal_PushBatch_FixedShift_VCPU=discard +fun:TcmallocSlab_Internal_PerCpuCmpxchg64=uninstrumented +fun:TcmallocSlab_Internal_PerCpuCmpxchg64=discard +fun:TcmallocSlab_Internal_PerCpuCmpxchg64_VCPU=uninstrumented +fun:TcmallocSlab_Internal_PerCpuCmpxchg64_VCPU=discard +fun:TcmallocSlab_Internal_PopBatch_FixedShift=uninstrumented +fun:TcmallocSlab_Internal_PopBatch_FixedShift=discard +fun:TcmallocSlab_Internal_PopBatch_FixedShift_VCPU=uninstrumented +fun:TcmallocSlab_Internal_PopBatch_FixedShift_VCPU=discard + # Ignores the dfsan wrappers. fun:__dfsw_*=uninstrumented fun:__dfsw_*=discard diff --git a/compiler-rt/test/dfsan/mmap_at_init.c b/compiler-rt/test/dfsan/mmap_at_init.c --- a/compiler-rt/test/dfsan/mmap_at_init.c +++ b/compiler-rt/test/dfsan/mmap_at_init.c @@ -1,7 +1,7 @@ // RUN: %clang_dfsan -fno-sanitize=dataflow -DCALLOC -c %s -o %t-calloc.o // RUN: %clang_dfsan %s %t-calloc.o -o %t // RUN: %run %t -// +// // REQUIRES: x86_64-target-arch // // Tests that calling mmap() during during dfsan initialization works.