From: Vyacheslav Cherkashin Date: Thu, 13 Dec 2018 14:17:58 +0000 (+0300) Subject: Implement adapter for LSan from tizen-mainline X-Git-Tag: accepted/tizen/unified/20190211.060129~6^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0d8d44ed5440816d7ae76c43bef7d05c6fedbc6d;p=platform%2Fcore%2Fsystem%2Fswap-probe.git Implement adapter for LSan from tizen-mainline This adapter implements the following: 1. Load LSan library 2. Contains a modified interceptors (because LSan not loaded first) 3. Inject interceptors to application 4. Provides an API for using LSan library Change-Id: Id33efe56b6ae3271858546050f1e2e8cfc8d250f Signed-off-by: Vyacheslav Cherkashin --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b17a80e..0843cc1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ set(SRC core/injector.cpp core/internal_libc.cpp core/internal_libdl.cpp + lsan/lsan_adapter.cpp ) add_library(${PROJECT_NAME} SHARED ${SRC}) diff --git a/src/core/internal_libc.cpp b/src/core/internal_libc.cpp index 9f744a5..bc6dfe7 100644 --- a/src/core/internal_libc.cpp +++ b/src/core/internal_libc.cpp @@ -1,10 +1,20 @@ #include // for ::mprotect() +#include // for ::malloc_usable_size() #include "internal_libc.h" namespace __swap { namespace internal { +void *memcpy(void *dest, const void *src, uptr n) noexcept +{ + char *d = (char*)dest; + const char *s = (const char *)src; + for (uptr i = 0; i < n; ++i) + d[i] = s[i]; + return dest; +} + int strcmp(const char *s1, const char *s2) noexcept { for (;; ++s1, ++s2) { @@ -24,5 +34,11 @@ int mprotect(void *addr, uptr len, int prot) noexcept return ::mprotect(addr, len, prot); } +uptr malloc_usable_size(void *ptr) noexcept +{ + // TODO: Replace it. + return ::malloc_usable_size(ptr); +} + } // namespace internal } // namespace __swap diff --git a/src/core/internal_libc.h b/src/core/internal_libc.h index f842d9b..f89be62 100644 --- a/src/core/internal_libc.h +++ b/src/core/internal_libc.h @@ -6,8 +6,10 @@ namespace __swap { namespace internal { +void *memcpy(void *dest, const void *src, uptr n) noexcept; int strcmp(const char *s1, const char *s2) noexcept; int mprotect(void *addr, uptr len, int prot) noexcept; +uptr malloc_usable_size(void *ptr) noexcept; } // namespace internal } // namespace __swap diff --git a/src/lsan/lsan_adapter.cpp b/src/lsan/lsan_adapter.cpp new file mode 100644 index 0000000..b30ac4d --- /dev/null +++ b/src/lsan/lsan_adapter.cpp @@ -0,0 +1,189 @@ +#include +#include +#include +#include +#include + +#include "lsan_adapter.h" + + +#define IS_CLEAR_MEMORY 1 +#define CALLER_PC (uptr)__builtin_return_address(0) +#define CURRENT_FRAME (uptr)__builtin_frame_address(0) + + +namespace __swap { +namespace lsan_adapter { + +// Declare external LSan API +static void *(*__lsan_allocator_allocate)(uptr size, uptr alignment, int cleared, + uptr caller_pc, uptr frame); +static void *(*__lsan_allocator_reallocate)(void *p, uptr new_size, uptr alignment, + uptr caller_pc, uptr frame); +static void (*__lsan_allocator_deallocate)(void *p); +static int (*__lsan_allocator_pointer_is_mine)(const void *p); + +static const char *(*__lsan_report_file)(); +static void (*__lsan_do_leak_check)(); + + +// TODO: Replace with calling a real free() +extern "C" void __libc_free(void *ptr); +static void (*real_free)(void *ptr) = __libc_free; + + +// ==================== +// Memory interceptors: +// ==================== +static void *interceptor_malloc(uptr size) +{ + return __lsan_allocator_allocate(size, 1, IS_CLEAR_MEMORY, CALLER_PC, CURRENT_FRAME); +} + +static void interceptor_free(void *ptr) +{ + if (__lsan_allocator_pointer_is_mine(ptr)) { + __lsan_allocator_deallocate(ptr); + } else { + real_free(ptr); + } +} + +static void *interceptor_calloc(uptr nmemb, uptr size) +{ + return __lsan_allocator_allocate(nmemb * size, 1, true, CALLER_PC, CURRENT_FRAME); +} + +static void *interceptor_realloc(void *ptr, uptr size) +{ + void *p; + + if (__lsan_allocator_pointer_is_mine(ptr)) { + p = __lsan_allocator_reallocate(ptr, size, 1, CALLER_PC, CURRENT_FRAME); + } else { + if (size == 0) { + real_free(ptr); + p = 0; + } else { + p = __lsan_allocator_allocate(size, 1, IS_CLEAR_MEMORY, CALLER_PC, CURRENT_FRAME); + + if (ptr) { + uptr real_size = internal::malloc_usable_size(ptr); + uptr copy_size = size < real_size ? size : real_size; + internal::memcpy(p, ptr, copy_size); + } + } + } + + return p; +} + + +static struct injector::injection_info info[] = { + INJECTION_INFO_INIT("malloc", interceptor_malloc), + INJECTION_INFO_INIT("free", interceptor_free), + INJECTION_INFO_INIT("cfree", interceptor_free), + INJECTION_INFO_INIT("calloc", interceptor_calloc), + INJECTION_INFO_INIT("realloc", interceptor_realloc), + +/* + TODO: Need implement: + INJECTION_INFO_INIT("memalign", interceptor_memalign); + INJECTION_INFO_INIT("posix_memalign", interceptor_posix_memalign); + INJECTION_INFO_INIT("__libc_memalign", interceptor___libc_memalign); + INJECTION_INFO_INIT("valloc", interceptor_valloc); + INJECTION_INFO_INIT("pvalloc", interceptor_pvalloc); + INJECTION_INFO_INIT("malloc_usable_size", interceptor_malloc_usable_size); + INJECTION_INFO_INIT("mallinfo", interceptor_mallinfo); + INJECTION_INFO_INIT("mallopt", interceptor_mallopt); + INJECTION_INFO_INIT("pthread_create", interceptor_pthread_create); + INJECTION_INFO_INIT("pthread_join", interceptor_pthread_join); + INJECTION_INFO_INIT("mmap", interceptor_mmap); + INJECTION_INFO_INIT("mmap64", interceptor_mmap64); + INJECTION_INFO_INIT("munmap", interceptor_munmap); +*/ +}; + +template +constexpr int ARRAY_SIZE(T(&)[N]) { return N; } + + +#define DLSYM_INIT_OR_RETURN(ret_value, handle, sym_name) \ + do { \ + sym_name = decltype(sym_name)(internal::dlsym(handle, #sym_name)); \ + if (!sym_name) { \ + LOGE("Symbol '" #sym_name "' not found."); \ + return ret_value; \ + } \ + } while (0) + +static bool init(const char *lsan_path) +{ + static bool is_lsan_init = false; + + if (is_lsan_init) { + LOGW("LsanAdapter already initialized"); + return false; + } + + void *handle = internal::dlopen(lsan_path, RTLD_NOW); + if (!handle) { + LOGE("Cannot load LSan library, path=%s", lsan_path); + return false; + } + + DLSYM_INIT_OR_RETURN(false, handle, __lsan_allocator_allocate); + DLSYM_INIT_OR_RETURN(false, handle, __lsan_allocator_deallocate); + DLSYM_INIT_OR_RETURN(false, handle, __lsan_allocator_reallocate); + DLSYM_INIT_OR_RETURN(false, handle, __lsan_allocator_pointer_is_mine); + DLSYM_INIT_OR_RETURN(false, handle, __lsan_do_leak_check); + DLSYM_INIT_OR_RETURN(false, handle, __lsan_report_file); + + injector::inject(info, ARRAY_SIZE(info)); + + is_lsan_init = true; + LOGI("LSan library has been loaded, path=%s", lsan_path); + return true; +} + +static void leak_check() +{ + LOGI("Start memory leaks checking"); + __lsan_do_leak_check(); + LOGI("Finish memory leaks checking"); +} + +static const char *report_file() +{ + return __lsan_report_file(); +} + +} // namespace lsan_adapter +} // namespace __swap + + +// ============= +// External API: +// ============= + +extern "C" SWAP_INTERFACE_ATTRIBUTE +int __swap_lsan_adapter_init(const char *lsan_path) +{ + if (!__swap::lsan_adapter::init(lsan_path)) { + LOGE("Cannot init LSan, path=%s", lsan_path); + return -1; + } + return 0; +} + +extern "C" SWAP_INTERFACE_ATTRIBUTE +void __swap_lsan_adapter_leak_check() +{ + __swap::lsan_adapter::leak_check(); +} + +extern "C" SWAP_INTERFACE_ATTRIBUTE +const char *__swap_lsan_adapter_report_file() +{ + return __swap::lsan_adapter::report_file(); +} diff --git a/src/lsan/lsan_adapter.h b/src/lsan/lsan_adapter.h new file mode 100644 index 0000000..9949181 --- /dev/null +++ b/src/lsan/lsan_adapter.h @@ -0,0 +1,16 @@ +#ifndef LSAN_ADAPTER_H +#define LSAN_ADAPTER_H + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +int __swap_lsan_adapter_init(const char *lsan_path); +void __swap_lsan_adapter_leak_check(); +const char *__swap_lsan_adapter_report_file(); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // LSAN_ADAPTER_H