From d6535ea4c4d49078a93735b315b8518fb692a592 Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Tue, 1 Apr 2014 13:16:30 +0000 Subject: [PATCH] [ASan] Optional support for dynamic ASan runtime on Linux. Based on http://llvm-reviews.chandlerc.com/D3042 by Yuri Gribov! git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@205308 91177308-0d34-0410-b5e6-96231b3b80d8 --- CMakeLists.txt | 2 + cmake/Modules/AddCompilerRT.cmake | 6 +- cmake/config-ix.cmake | 1 + lib/asan/CMakeLists.txt | 51 +++++++++++++-- lib/asan/asan_internal.h | 6 ++ lib/asan/asan_linux.cc | 76 +++++++++++++++++++++- lib/asan/asan_mac.cc | 6 ++ lib/asan/asan_rtl.cc | 18 +++++ lib/asan/asan_win.cc | 4 ++ lib/asan/tests/CMakeLists.txt | 16 +++++ test/asan/CMakeLists.txt | 26 ++++++-- test/asan/TestCases/Linux/asan_dlopen_test.cc | 14 ++++ test/asan/TestCases/Linux/asan_preload_test-1.cc | 27 ++++++++ test/asan/TestCases/Linux/asan_preload_test-2.cc | 21 ++++++ .../asan/TestCases/Linux/asan_rt_confict_test-1.cc | 12 ++++ .../asan/TestCases/Linux/asan_rt_confict_test-2.cc | 24 +++++++ .../asan/TestCases/Linux/interface_symbols_linux.c | 2 +- test/asan/TestCases/sanity_check_pure_c.c | 2 +- test/asan/Unit/lit.site.cfg.in | 10 +++ test/asan/lit.cfg | 40 ++++++++---- test/asan/lit.site.cfg.in | 2 + test/lit.common.configured.in | 2 + unittests/lit.common.unit.configured.in | 1 + 23 files changed, 345 insertions(+), 24 deletions(-) create mode 100644 test/asan/TestCases/Linux/asan_dlopen_test.cc create mode 100644 test/asan/TestCases/Linux/asan_preload_test-1.cc create mode 100644 test/asan/TestCases/Linux/asan_preload_test-2.cc create mode 100644 test/asan/TestCases/Linux/asan_rt_confict_test-1.cc create mode 100644 test/asan/TestCases/Linux/asan_rt_confict_test-2.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index a2d40d97b..6190be404 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -231,6 +231,8 @@ option(COMPILER_RT_DEBUG "Build runtimes with full debug info" OFF) # COMPILER_RT_DEBUG_PYBOOL is used by lit.common.configured.in. pythonize_bool(COMPILER_RT_DEBUG) +option(COMPILER_RT_BUILD_SHARED_ASAN "Build shared version of AddressSanitizer runtime" OFF) + #================================ # Setup Compiler Flags #================================ diff --git a/cmake/Modules/AddCompilerRT.cmake b/cmake/Modules/AddCompilerRT.cmake index 556ada48a..82947eac4 100644 --- a/cmake/Modules/AddCompilerRT.cmake +++ b/cmake/Modules/AddCompilerRT.cmake @@ -45,7 +45,7 @@ endmacro() # DEFS ) macro(add_compiler_rt_runtime name arch type) if(CAN_TARGET_${arch}) - parse_arguments(LIB "SOURCES;CFLAGS;DEFS" "" ${ARGN}) + parse_arguments(LIB "SOURCES;CFLAGS;DEFS;OUTPUT_NAME" "" ${ARGN}) add_library(${name} ${type} ${LIB_SOURCES}) # Setup compile flags and definitions. set_target_compile_flags(${name} @@ -58,6 +58,10 @@ macro(add_compiler_rt_runtime name arch type) set_target_properties(${name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR} LIBRARY_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) + if (LIB_OUTPUT_NAME) + set_target_properties(${name} PROPERTIES + OUTPUT_NAME ${LIB_OUTPUT_NAME}) + endif() # Add installation command. install(TARGETS ${name} ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR} diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake index d9cc7f0e7..fe194d50d 100644 --- a/cmake/config-ix.cmake +++ b/cmake/config-ix.cmake @@ -15,6 +15,7 @@ check_cxx_compiler_flag(-fno-rtti COMPILER_RT_HAS_FNO_RTTI_FLAG) check_cxx_compiler_flag(-ffreestanding COMPILER_RT_HAS_FFREESTANDING_FLAG) check_cxx_compiler_flag("-Werror -fno-function-sections" COMPILER_RT_HAS_FNO_FUNCTION_SECTIONS_FLAG) check_cxx_compiler_flag(-std=c++11 COMPILER_RT_HAS_STD_CXX11_FLAG) +check_cxx_compiler_flag(-ftls-model=initial-exec COMPILER_RT_HAS_FTLS_MODEL_INITIAL_EXEC) check_cxx_compiler_flag(/GR COMPILER_RT_HAS_GR_FLAG) check_cxx_compiler_flag(/GS COMPILER_RT_HAS_GS_FLAG) diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt index d1c0d801c..88647b841 100644 --- a/lib/asan/CMakeLists.txt +++ b/lib/asan/CMakeLists.txt @@ -21,7 +21,6 @@ set(ASAN_SOURCES asan_new_delete.cc asan_poisoning.cc asan_posix.cc - asan_preinit.cc asan_report.cc asan_rtl.cc asan_stack.cc @@ -29,6 +28,9 @@ set(ASAN_SOURCES asan_thread.cc asan_win.cc) +set(ASAN_PREINIT_SOURCES + asan_preinit.cc) + include_directories(..) if(ANDROID) @@ -46,6 +48,17 @@ if(ANDROID) ASAN_LOW_MEMORY=1) endif() +set(ASAN_DYNAMIC_DEFINITIONS + ${ASAN_COMMON_DEFINITIONS} ASAN_DYNAMIC=1) + +set(ASAN_DYNAMIC_CFLAGS ${ASAN_CFLAGS}) +append_if(COMPILER_RT_HAS_FTLS_MODEL_INITIAL_EXEC + -ftls-model=initial-exec ASAN_DYNAMIC_CFLAGS) + +set(ASAN_DYNAMIC_LIBS stdc++ m c) +append_if(COMPILER_RT_HAS_LIBPTHREAD pthread ASAN_DYNAMIC_LIBS) +append_if(COMPILER_RT_HAS_LIBDL dl ASAN_DYNAMIC_LIBS) + if (NOT MSVC) set(ASAN_ASM_SOURCES asan_asm_instrumentation.S) set_source_files_properties(${ASAN_ASM_SOURCES} PROPERTIES LANGUAGE C) @@ -71,6 +84,14 @@ else() add_compiler_rt_object_library(RTAsan ${arch} SOURCES ${ASAN_SOURCES} CFLAGS ${ASAN_CFLAGS} DEFS ${ASAN_COMMON_DEFINITIONS}) + add_compiler_rt_object_library(RTAsan_preinit ${arch} + SOURCES ${ASAN_PREINIT_SOURCES} CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + if (COMPILER_RT_BUILD_SHARED_ASAN) + add_compiler_rt_object_library(RTAsan_dynamic ${arch} + SOURCES ${ASAN_SOURCES} CFLAGS ${ASAN_DYNAMIC_CFLAGS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS}) + endif() endforeach() endif() @@ -103,21 +124,41 @@ elseif(ANDROID) else() # Build separate libraries for each target. foreach(arch ${ASAN_SUPPORTED_ARCH}) - set(ASAN_RUNTIME_OBJECTS - $ + set(ASAN_COMMON_RUNTIME_OBJECTS $ $ $) if (NOT WIN32) # We can't build Leak Sanitizer on Windows yet. - list(APPEND ASAN_RUNTIME_OBJECTS $) + list(APPEND ASAN_COMMON_RUNTIME_OBJECTS + $) endif() add_compiler_rt_runtime(clang_rt.asan-${arch} ${arch} STATIC - SOURCES ${ASAN_RUNTIME_OBJECTS} + SOURCES $ + $ + ${ASAN_COMMON_RUNTIME_OBJECTS} CFLAGS ${ASAN_CFLAGS} DEFS ${ASAN_COMMON_DEFINITIONS}) add_dependencies(asan clang_rt.asan-${arch}) + + if (COMPILER_RT_BUILD_SHARED_ASAN) + add_compiler_rt_runtime(clang_rt.asan-preinit-${arch} ${arch} STATIC + SOURCES $ + CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + add_dependencies(asan clang_rt.asan-preinit-${arch}) + + add_compiler_rt_runtime(clang_rt.asan-dynamic-${arch} ${arch} SHARED + OUTPUT_NAME clang_rt.asan-${arch} + SOURCES $ + ${ASAN_COMMON_RUNTIME_OBJECTS} + CFLAGS ${ASAN_DYNAMIC_CFLAGS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS}) + target_link_libraries(clang_rt.asan-dynamic-${arch} ${ASAN_DYNAMIC_LIBS}) + add_dependencies(asan clang_rt.asan-dynamic-${arch}) + endif() + if (UNIX AND NOT ${arch} STREQUAL "i386") add_sanitizer_rt_symbols(clang_rt.asan-${arch} asan.syms.extra) add_dependencies(asan clang_rt.asan-${arch}-symbols) diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h index 4dbcae193..4934d6cd7 100644 --- a/lib/asan/asan_internal.h +++ b/lib/asan/asan_internal.h @@ -49,6 +49,10 @@ # define ASAN_USE_PREINIT_ARRAY (SANITIZER_LINUX && !SANITIZER_ANDROID) #endif +#ifndef ASAN_DYNAMIC +# define ASAN_DYNAMIC 0 +#endif + // All internal functions in asan reside inside the __asan namespace // to avoid namespace collisions with the user programs. // Seperate namespace also makes it simpler to distinguish the asan run-time @@ -69,6 +73,8 @@ void ReplaceSystemMalloc(); // asan_linux.cc / asan_mac.cc / asan_win.cc void *AsanDoesNotSupportStaticLinkage(); +void AsanCheckDynamicRTPrereqs(); +void AsanCheckIncompatibleRT(); void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); void AsanOnSIGSEGV(int, void *siginfo, void *context); diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc index 5529f56bd..c6085aa7b 100644 --- a/lib/asan/asan_linux.cc +++ b/lib/asan/asan_linux.cc @@ -35,8 +35,11 @@ #if SANITIZER_ANDROID #include +extern "C" void* _DYNAMIC; #else #include +#include +#include #endif // x86_64 FreeBSD 9.2 and older define 64-bit register names in both 64-bit @@ -50,7 +53,17 @@ # endif #endif -extern "C" void* _DYNAMIC; +typedef enum { + ASAN_RT_VERSION_UNDEFINED = 0, + ASAN_RT_VERSION_DYNAMIC, + ASAN_RT_VERSION_STATIC, +} asan_rt_version_t; + +// FIXME: perhaps also store abi version here? +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +asan_rt_version_t __asan_rt_version; +} namespace __asan { @@ -63,6 +76,67 @@ void *AsanDoesNotSupportStaticLinkage() { return &_DYNAMIC; // defined in link.h } +static int FindFirstDSOCallback(struct dl_phdr_info *info, size_t size, + void *data) { + // Continue until the first dynamic library is found + if (!info->dlpi_name || info->dlpi_name[0] == 0) + return 0; + + *(const char **)data = info->dlpi_name; + return 1; +} + +static bool IsDynamicRTName(const char *libname) { + return internal_strstr(libname, "libclang_rt.asan") || + internal_strstr(libname, "libasan.so"); +} + +void AsanCheckDynamicRTPrereqs() { + // FIXME: can we do something like this for Android? +#if !SANITIZER_ANDROID + // Ensure that dynamic RT is the first DSO in the list + const char *first_dso_name = 0; + dl_iterate_phdr(FindFirstDSOCallback, &first_dso_name); + if (first_dso_name && !IsDynamicRTName(first_dso_name)) { + Report("ASan runtime does not come first in initial library list; " + "you should either link runtime to your application or " + "manually preload it with LD_PRELOAD.\n"); + Die(); + } +#endif +} + +void AsanCheckIncompatibleRT() { +#if !SANITIZER_ANDROID + if (ASAN_DYNAMIC) { + if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) { + __asan_rt_version = ASAN_RT_VERSION_DYNAMIC; + } else if (__asan_rt_version != ASAN_RT_VERSION_DYNAMIC) { + Report("Your application is linked against " + "incompatible ASan runtimes.\n"); + Die(); + } + } else { + // Ensure that dynamic runtime is not present. We should detect it + // as early as possible, otherwise ASan interceptors could bind to + // the functions in dynamic ASan runtime instead of the functions in + // system libraries, causing crashes later in ASan initialization. + MemoryMappingLayout proc_maps(/*cache_enabled*/true); + char filename[128]; + while (proc_maps.Next(0, 0, 0, filename, sizeof(filename), 0)) { + if (IsDynamicRTName(filename)) { + Report("Your application is linked against " + "incompatible ASan runtimes.\n"); + Die(); + } + } + + CHECK_NE(__asan_rt_version, ASAN_RT_VERSION_DYNAMIC); + __asan_rt_version = ASAN_RT_VERSION_STATIC; + } +#endif +} + void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { #if defined(__arm__) ucontext_t *ucontext = (ucontext_t*)context; diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc index 0428a1845..9f2fabd69 100644 --- a/lib/asan/asan_mac.cc +++ b/lib/asan/asan_mac.cc @@ -199,6 +199,12 @@ void *AsanDoesNotSupportStaticLinkage() { return 0; } +// No-op. Mac does not support static linkage anyway. +void AsanCheckDynamicRTPrereqs() {} + +// No-op. Mac does not support static linkage anyway. +void AsanCheckIncompatibleRT() {} + bool AsanInterceptsSignal(int signum) { return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv; diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc index 631b5c955..2d93a8897 100644 --- a/lib/asan/asan_rtl.cc +++ b/lib/asan/asan_rtl.cc @@ -637,6 +637,23 @@ void AsanInitFromRtl() { AsanInitInternal(); } +#if ASAN_DYNAMIC +// Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable +// (and thus normal initializer from .preinit_array haven't run). + +class AsanInitializer { +public: // NOLINT + AsanInitializer() { + AsanCheckIncompatibleRT(); + AsanCheckDynamicRTPrereqs(); + if (!asan_inited) + __asan_init(); + } +}; + +static AsanInitializer asan_initializer; +#endif // ASAN_DYNAMIC + } // namespace __asan // ---------------------- Interface ---------------- {{{1 @@ -688,6 +705,7 @@ void NOINLINE __asan_set_death_callback(void (*callback)(void)) { // Initialize as requested from instrumented application code. // We use this call as a trigger to wake up ASan from deactivated state. void __asan_init() { + AsanCheckIncompatibleRT(); AsanActivate(); AsanInitInternal(); } diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc index 54fec2b14..da26e989a 100644 --- a/lib/asan/asan_win.cc +++ b/lib/asan/asan_win.cc @@ -70,6 +70,10 @@ void *AsanDoesNotSupportStaticLinkage() { return 0; } +void AsanCheckDynamicRTPrereqs() { UNIMPLEMENTED(); } + +void AsanCheckIncompatibleRT() {} + void AsanPlatformThreadInit() { // Nothing here for now. } diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt index e01d6ea8a..e85cfaf88 100644 --- a/lib/asan/tests/CMakeLists.txt +++ b/lib/asan/tests/CMakeLists.txt @@ -66,10 +66,16 @@ if(NOT ANDROID) list(APPEND ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS -fsanitize=address) endif() +set(ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS + ${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS} + -shared-libasan) + set(ASAN_UNITTEST_NOINST_LINKFLAGS ${ASAN_UNITTEST_COMMON_LINKFLAGS} -lm) append_if(COMPILER_RT_HAS_LIBDL -ldl ASAN_UNITTEST_NOINST_LINKFLAGS) append_if(COMPILER_RT_HAS_LIBPTHREAD -lpthread ASAN_UNITTEST_NOINST_LINKFLAGS) +append_if(COMPILER_RT_HAS_LIBPTHREAD -lpthread + ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS) # Compile source for the given architecture, using compiler # options in ${ARGN}, and add it to the object list. @@ -154,6 +160,11 @@ macro(add_asan_tests_for_arch arch) add_asan_test(AsanUnitTests "Asan-${arch}-Test" ${arch} OBJECTS ${ASAN_INST_TEST_OBJECTS} LINKFLAGS ${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS}) + if(COMPILER_RT_BUILD_SHARED_ASAN) + add_asan_test(AsanUnitTests "Asan-${arch}-Dynamic-Test" ${arch} + OBJECTS ${ASAN_INST_TEST_OBJECTS} + LINKFLAGS ${ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS}) + endif() # Add static ASan runtime that will be linked with uninstrumented tests. set(ASAN_TEST_RUNTIME RTAsanTest.${arch}) @@ -194,6 +205,11 @@ macro(add_asan_tests_for_arch arch) add_asan_test(AsanBenchmarks "Asan-${arch}-Benchmark" ${arch} OBJECTS ${ASAN_BENCHMARKS_OBJECTS} LINKFLAGS ${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS}) + if(COMPILER_RT_BUILD_SHARED_ASAN) + add_asan_test(AsanBenchmarks "Asan-${arch}-Dynamic-Benchmark" ${arch} + OBJECTS ${ASAN_BENCHMARKS_OBJECTS} + LINKFLAGS ${ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS}) + endif() endmacro() if(COMPILER_RT_CAN_EXECUTE_TESTS) diff --git a/test/asan/CMakeLists.txt b/test/asan/CMakeLists.txt index 954fc7065..0dd6f816a 100644 --- a/test/asan/CMakeLists.txt +++ b/test/asan/CMakeLists.txt @@ -21,29 +21,47 @@ if(CAN_TARGET_x86_64 OR CAN_TARGET_powerpc64) set(ASAN_TEST_CONFIG_SUFFIX "64") set(ASAN_TEST_BITS "64") set(ASAN_TEST_TARGET_CFLAGS ${TARGET_64_BIT_CFLAGS}) + set(ASAN_TEST_DYNAMIC False) configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig/lit.site.cfg ) list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig) + if(COMPILER_RT_BUILD_SHARED_ASAN) + set(ASAN_TEST_CONFIG_SUFFIX "64-Dynamic") + set(ASAN_TEST_DYNAMIC True) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig-dynamic/lit.site.cfg) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig-dynamic) + endif() endif() if(CAN_TARGET_i386) set(ASAN_TEST_CONFIG_SUFFIX "32") set(ASAN_TEST_BITS "32") set(ASAN_TEST_TARGET_CFLAGS ${TARGET_32_BIT_CFLAGS}) + set(ASAN_TEST_DYNAMIC False) + set(ASAN_TEST_TARGET_ARCH "i386") configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig/lit.site.cfg ) list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig) + if(COMPILER_RT_BUILD_SHARED_ASAN) + set(ASAN_TEST_CONFIG_SUFFIX "32-Dynamic") + set(ASAN_TEST_DYNAMIC True) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic/lit.site.cfg) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic) + endif() endif() if(COMPILER_RT_INCLUDE_TESTS) -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg - ) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg) endif() set(ASAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) diff --git a/test/asan/TestCases/Linux/asan_dlopen_test.cc b/test/asan/TestCases/Linux/asan_dlopen_test.cc new file mode 100644 index 000000000..83ce4f83b --- /dev/null +++ b/test/asan/TestCases/Linux/asan_dlopen_test.cc @@ -0,0 +1,14 @@ +// Test that dlopen of dynamic runtime is prohibited. +// +// RUN: %clangxx %s -DRT=\"%shared_libasan\" -o %t -ldl +// RUN: not %t 2>&1 | FileCheck %s +// REQUIRES: asan-dynamic-runtime + +#include + +int main(int argc, char **argv) { + dlopen(RT, RTLD_LAZY); + return 0; +} + +// CHECK: ASan runtime does not come first in initial library list diff --git a/test/asan/TestCases/Linux/asan_preload_test-1.cc b/test/asan/TestCases/Linux/asan_preload_test-1.cc new file mode 100644 index 000000000..f6eff8ee1 --- /dev/null +++ b/test/asan/TestCases/Linux/asan_preload_test-1.cc @@ -0,0 +1,27 @@ +// Test that non-sanitized executables work with sanitized shared libs +// and preloaded runtime. +// +// RUN: %clangxx -DBUILD_SO=1 -fPIC -shared %s -o %t.so +// RUN: %clangxx %s %t.so -o %t +// +// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so +// RUN: LD_PRELOAD=%shared_libasan not %t 2>&1 | FileCheck %s + +// REQUIRES: asan-dynamic-runtime + +#if BUILD_SO +char dummy; +void do_access(const void *p) { + // CHECK: AddressSanitizer: heap-buffer-overflow + dummy = ((const char *)p)[1]; +} +#else +#include +extern void do_access(const void *p); +int main(int argc, char **argv) { + void *p = malloc(1); + do_access(p); + free(p); + return 0; +} +#endif diff --git a/test/asan/TestCases/Linux/asan_preload_test-2.cc b/test/asan/TestCases/Linux/asan_preload_test-2.cc new file mode 100644 index 000000000..a0f88ce93 --- /dev/null +++ b/test/asan/TestCases/Linux/asan_preload_test-2.cc @@ -0,0 +1,21 @@ +// Test that preloaded runtime works with unsanitized executables. +// +// RUN: %clangxx %s -o %t +// RUN: LD_PRELOAD=%shared_libasan not %t 2>&1 | FileCheck %s + +// REQUIRES: asan-dynamic-runtime + +#include + +extern "C" void *memset(void *p, int val, size_t n); + +void do_access(void *p) { + // CHECK: AddressSanitizer: heap-buffer-overflow + memset(p, 0, 2); +} + +int main(int argc, char **argv) { + void *p = malloc(1); + do_access(p); + return 0; +} diff --git a/test/asan/TestCases/Linux/asan_rt_confict_test-1.cc b/test/asan/TestCases/Linux/asan_rt_confict_test-1.cc new file mode 100644 index 000000000..a66aaf2a8 --- /dev/null +++ b/test/asan/TestCases/Linux/asan_rt_confict_test-1.cc @@ -0,0 +1,12 @@ +// Test that preloading dynamic runtime to statically sanitized +// executable is prohibited. +// +// RUN: %clangxx_asan_static %s -o %t +// RUN: LD_PRELOAD=%shared_libasan not %t 2>&1 | FileCheck %s + +// REQUIRES: asan-dynamic-runtime + +#include +int main(int argc, char **argv) { return 0; } + +// CHECK: Your application is linked against incompatible ASan runtimes diff --git a/test/asan/TestCases/Linux/asan_rt_confict_test-2.cc b/test/asan/TestCases/Linux/asan_rt_confict_test-2.cc new file mode 100644 index 000000000..3898e7bb1 --- /dev/null +++ b/test/asan/TestCases/Linux/asan_rt_confict_test-2.cc @@ -0,0 +1,24 @@ +// Test that mixed static/dynamic sanitization of program objects +// is prohibited. +// +// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so +// RUN: %clangxx_asan_static %s %t.so -o %t +// RUN: not %t 2>&1 | FileCheck %s + +// REQUIRES: asan-dynamic-runtime + +#if BUILD_SO +char dummy; +void do_access(const void *p) { dummy = ((const char *)p)[1]; } +#else +#include +extern void do_access(const void *p); +int main(int argc, char **argv) { + void *p = malloc(1); + do_access(p); + free(p); + return 0; +} +#endif + +// CHECK: Your application is linked against incompatible ASan runtimes diff --git a/test/asan/TestCases/Linux/interface_symbols_linux.c b/test/asan/TestCases/Linux/interface_symbols_linux.c index f67a12476..4764ef7f0 100644 --- a/test/asan/TestCases/Linux/interface_symbols_linux.c +++ b/test/asan/TestCases/Linux/interface_symbols_linux.c @@ -31,6 +31,6 @@ // FIXME: nm -D on powerpc somewhy shows ASan interface symbols residing // in "initialized data section". -// REQUIRES: x86_64-supported-target,i386-supported-target +// REQUIRES: x86_64-supported-target,i386-supported-target,asan-static-runtime int main() { return 0; } diff --git a/test/asan/TestCases/sanity_check_pure_c.c b/test/asan/TestCases/sanity_check_pure_c.c index df150675b..6065532a5 100644 --- a/test/asan/TestCases/sanity_check_pure_c.c +++ b/test/asan/TestCases/sanity_check_pure_c.c @@ -3,7 +3,7 @@ // RUN: not %t 2>&1 | FileCheck %s // Sanity checking a test in pure C with -pie. -// RUN: %clang_asan -O2 %s -pie -o %t +// RUN: %clang_asan -O2 %s -pie -fPIE -o %t // RUN: not %t 2>&1 | FileCheck %s #include diff --git a/test/asan/Unit/lit.site.cfg.in b/test/asan/Unit/lit.site.cfg.in index 96cfc386a..46eea1ff9 100644 --- a/test/asan/Unit/lit.site.cfg.in +++ b/test/asan/Unit/lit.site.cfg.in @@ -1,9 +1,16 @@ ## Autogenerated by LLVM/Clang configuration. # Do not edit! +import os + # Load common config for all compiler-rt unit tests. lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured") +def push_ld_library_path(config, new_path): + new_ld_library_path = os.path.pathsep.join( + (new_path, config.environment['LD_LIBRARY_PATH'])) + config.environment['LD_LIBRARY_PATH'] = new_ld_library_path + # Setup config name. config.name = 'AddressSanitizer-Unit' @@ -13,6 +20,9 @@ config.name = 'AddressSanitizer-Unit' config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/asan/tests" config.test_source_root = config.test_exec_root +# Set LD_LIBRARY_PATH to pick dynamic runtime up properly. +push_ld_library_path(config, config.compiler_rt_libdir) + # Enable leak detection in ASan unit tests on x86_64-linux. if config.host_os == 'Linux' and config.host_arch == 'x86_64': config.environment['ASAN_OPTIONS'] = 'detect_leaks=1' diff --git a/test/asan/lit.cfg b/test/asan/lit.cfg index f24e68037..7f47b43ed 100644 --- a/test/asan/lit.cfg +++ b/test/asan/lit.cfg @@ -11,6 +11,11 @@ def get_required_attr(config, attr_name): "to lit.site.cfg " % attr_name) return attr_value +def push_ld_library_path(config, new_path): + new_ld_library_path = os.path.pathsep.join( + (new_path, config.environment['LD_LIBRARY_PATH'])) + config.environment['LD_LIBRARY_PATH'] = new_ld_library_path + # Setup config name. config.name = 'AddressSanitizer' + config.name_suffix @@ -27,12 +32,21 @@ else: # FIXME: Review the set of required flags and check if it can be reduced. target_cflags = [get_required_attr(config, "target_cflags")] + extra_linkflags target_cxxflags = config.cxx_mode_flags + target_cflags -clang_asan_cflags = ["-fsanitize=address", - "-mno-omit-leaf-frame-pointer", - "-fno-omit-frame-pointer", - "-fno-optimize-sibling-calls", - "-g"] + target_cflags -clang_asan_cxxflags = config.cxx_mode_flags + clang_asan_cflags +clang_asan_static_cflags = ["-fsanitize=address", + "-mno-omit-leaf-frame-pointer", + "-fno-omit-frame-pointer", + "-fno-optimize-sibling-calls", + "-g"] + target_cflags +clang_asan_static_cxxflags = config.cxx_mode_flags + clang_asan_static_cflags + +if config.asan_dynamic: + clang_asan_cflags = clang_asan_static_cflags + ['-shared-libasan'] + clang_asan_cxxflags = clang_asan_static_cxxflags + ['-shared-libasan'] + config.available_features.add("asan-dynamic-runtime") +else: + clang_asan_cflags = clang_asan_static_cflags + clang_asan_cxxflags = clang_asan_static_cxxflags + config.available_features.add("asan-static-runtime") asan_lit_source_dir = get_required_attr(config, "asan_lit_source_dir") if config.android == "TRUE": @@ -49,6 +63,10 @@ config.substitutions.append( ("%clang ", build_invocation(target_cflags)) ) config.substitutions.append( ("%clangxx ", build_invocation(target_cxxflags)) ) config.substitutions.append( ("%clang_asan ", build_invocation(clang_asan_cflags)) ) config.substitutions.append( ("%clangxx_asan ", build_invocation(clang_asan_cxxflags)) ) +config.substitutions.append( ("%shared_libasan", "libclang_rt.asan-%s.so" % config.target_arch)) +if config.asan_dynamic: + config.substitutions.append( ("%clang_asan_static ", build_invocation(clang_asan_static_cflags)) ) + config.substitutions.append( ("%clangxx_asan_static ", build_invocation(clang_asan_static_cxxflags)) ) # FIXME: De-hardcode this path. asan_source_dir = os.path.join( @@ -76,14 +94,14 @@ config.available_features.add("asan-" + config.bits + "-bits") if config.host_os == 'Linux' and config.bits == '64': config.environment['ASAN_OPTIONS'] = 'detect_leaks=1' -# GCC-ASan uses dynamic runtime by default, so we have to set LD_LIBRARY_PATH -# to pick it up properly. +# Set LD_LIBRARY_PATH to pick dynamic runtime up properly. +push_ld_library_path(config, config.compiler_rt_libdir) + +# GCC-ASan uses dynamic runtime by default. if config.compiler_id == 'GNU': gcc_dir = os.path.dirname(config.clang) libasan_dir = os.path.join(gcc_dir, "..", "lib" + config.bits) - new_ld_library_path = os.path.pathsep.join( - (libasan_dir, config.environment['LD_LIBRARY_PATH'])) - config.environment['LD_LIBRARY_PATH'] = new_ld_library_path + push_ld_library_path(config, libasan_dir) # Default test suffixes. config.suffixes = ['.c', '.cc', '.cpp'] diff --git a/test/asan/lit.site.cfg.in b/test/asan/lit.site.cfg.in index 48ea81252..76e0c5542 100644 --- a/test/asan/lit.site.cfg.in +++ b/test/asan/lit.site.cfg.in @@ -9,6 +9,8 @@ config.clang = "@ASAN_TEST_TARGET_CC@" config.llvm_tools_dir = "@ASAN_TEST_LLVM_TOOLS_DIR@" config.bits = "@ASAN_TEST_BITS@" config.android = "@CAN_TARGET_arm_android@" +config.asan_dynamic = @ASAN_TEST_DYNAMIC@ +config.target_arch = "@ASAN_TEST_TARGET_ARCH@" # Load common config for all compiler-rt lit tests. lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") diff --git a/test/lit.common.configured.in b/test/lit.common.configured.in index f33be90e7..7a9dd29b2 100644 --- a/test/lit.common.configured.in +++ b/test/lit.common.configured.in @@ -9,6 +9,7 @@ def set_default(attr, value): # Generic config options for all compiler-rt lit tests. set_default("target_triple", "@TARGET_TRIPLE@") set_default("host_arch", "@HOST_ARCH@") +set_default("target_arch", "@HOST_ARCH@") set_default("host_os", "@HOST_OS@") set_default("llvm_build_mode", "@LLVM_BUILD_MODE@") set_default("llvm_src_root", "@LLVM_SOURCE_DIR@") @@ -20,6 +21,7 @@ set_default("compiler_id", "@COMPILER_RT_TEST_COMPILER_ID@") set_default("compiler_rt_arch", "@COMPILER_RT_SUPPORTED_ARCH@") set_default("python_executable", "@PYTHON_EXECUTABLE@") set_default("compiler_rt_debug", @COMPILER_RT_DEBUG_PYBOOL@) +set_default("compiler_rt_libdir", "@COMPILER_RT_LIBRARY_OUTPUT_DIR@") # LLVM tools dir can be passed in lit parameters, so try to # apply substitution. diff --git a/unittests/lit.common.unit.configured.in b/unittests/lit.common.unit.configured.in index fe827c638..ab5c253c7 100644 --- a/unittests/lit.common.unit.configured.in +++ b/unittests/lit.common.unit.configured.in @@ -7,6 +7,7 @@ config.llvm_src_root = "@LLVM_SOURCE_DIR@" config.llvm_obj_root = "@LLVM_BINARY_DIR@" config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" +config.compiler_rt_libdir = "@COMPILER_RT_LIBRARY_OUTPUT_DIR@" config.llvm_build_mode = "@LLVM_BUILD_MODE@" config.host_arch = "@HOST_ARCH@" config.host_os = "@HOST_OS@" -- 2.11.4.GIT