From 7fb7330469af52ae1313b2b47c273e62c61a4dd5 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 24 May 2012 13:54:31 +0000 Subject: [PATCH] asan/tsan: weak interceptors The idea isthat asan/tsan can survive if user intercepts the same functions. At the same time user has an ability to call back into asan/tsan runtime. See the following tests for examples: asan/output_tests/interception_failure_test-linux.cc asan/output_tests/interception_test-linux.cc asan/output_tests/interception_malloc_test-linux.cc llvm-svn: 157388 --- compiler-rt/lib/asan/asan_interceptors.cc | 63 ++++++++++++---------- compiler-rt/lib/asan/output_tests/heap-overflow.cc | 2 +- .../interception_failure_test-linux.cc | 17 ++++++ .../output_tests/interception_malloc_test-linux.cc | 19 +++++++ .../asan/output_tests/interception_test-linux.cc | 18 +++++++ .../lib/asan/output_tests/strncpy-overflow.cc | 4 +- compiler-rt/lib/asan/output_tests/test_output.sh | 10 ++-- .../lib/asan/output_tests/use-after-free.cc | 4 +- compiler-rt/lib/interception/interception.h | 25 ++++++--- compiler-rt/lib/interception/interception_linux.cc | 5 +- compiler-rt/lib/interception/interception_linux.h | 6 ++- compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc | 2 +- 12 files changed, 126 insertions(+), 49 deletions(-) create mode 100644 compiler-rt/lib/asan/output_tests/interception_failure_test-linux.cc create mode 100644 compiler-rt/lib/asan/output_tests/interception_malloc_test-linux.cc create mode 100644 compiler-rt/lib/asan/output_tests/interception_test-linux.cc diff --git a/compiler-rt/lib/asan/asan_interceptors.cc b/compiler-rt/lib/asan/asan_interceptors.cc index 6d7e63b..9db6e75 100644 --- a/compiler-rt/lib/asan/asan_interceptors.cc +++ b/compiler-rt/lib/asan/asan_interceptors.cc @@ -743,6 +743,11 @@ INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT } #endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL +#define ASAN_INTERCEPT_FUNC(name) do { \ + if (!INTERCEPT_FUNCTION(name) && FLAG_v > 0) \ + Report("AddressSanitizer: failed to intercept '" #name "'\n"); \ + } while (0) + #if defined(_WIN32) INTERCEPTOR_WINAPI(DWORD, CreateThread, void* security, size_t stack_size, @@ -758,7 +763,7 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread, namespace __asan { void InitializeWindowsInterceptors() { - CHECK(INTERCEPT_FUNCTION(CreateThread)); + ASAN_INTERCEPT_FUNC(CreateThread); } } // namespace __asan @@ -771,65 +776,65 @@ void InitializeAsanInterceptors() { CHECK(was_called_once == false); was_called_once = true; // Intercept mem* functions. - CHECK(INTERCEPT_FUNCTION(memcmp)); - CHECK(INTERCEPT_FUNCTION(memmove)); - CHECK(INTERCEPT_FUNCTION(memset)); + ASAN_INTERCEPT_FUNC(memcmp); + ASAN_INTERCEPT_FUNC(memmove); + ASAN_INTERCEPT_FUNC(memset); if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { - CHECK(INTERCEPT_FUNCTION(memcpy)); + ASAN_INTERCEPT_FUNC(memcpy); } else { REAL(memcpy) = REAL(memmove); } // Intercept str* functions. - CHECK(INTERCEPT_FUNCTION(strcat)); // NOLINT - CHECK(INTERCEPT_FUNCTION(strchr)); - CHECK(INTERCEPT_FUNCTION(strcmp)); - CHECK(INTERCEPT_FUNCTION(strcpy)); // NOLINT - CHECK(INTERCEPT_FUNCTION(strlen)); - CHECK(INTERCEPT_FUNCTION(strncmp)); - CHECK(INTERCEPT_FUNCTION(strncpy)); + ASAN_INTERCEPT_FUNC(strcat); // NOLINT + ASAN_INTERCEPT_FUNC(strchr); + ASAN_INTERCEPT_FUNC(strcmp); + ASAN_INTERCEPT_FUNC(strcpy); // NOLINT + ASAN_INTERCEPT_FUNC(strlen); + ASAN_INTERCEPT_FUNC(strncmp); + ASAN_INTERCEPT_FUNC(strncpy); #if !defined(_WIN32) - CHECK(INTERCEPT_FUNCTION(strcasecmp)); - CHECK(INTERCEPT_FUNCTION(strdup)); - CHECK(INTERCEPT_FUNCTION(strncasecmp)); + ASAN_INTERCEPT_FUNC(strcasecmp); + ASAN_INTERCEPT_FUNC(strdup); + ASAN_INTERCEPT_FUNC(strncasecmp); # ifndef __APPLE__ - CHECK(INTERCEPT_FUNCTION(index)); + ASAN_INTERCEPT_FUNC(index); # else - CHECK(OVERRIDE_FUNCTION(index, WRAP(strchr))); + CHECK(OVERRIDE_FUNCTION(index, WRAP(strchr)); # endif #endif #if ASAN_INTERCEPT_STRNLEN - CHECK(INTERCEPT_FUNCTION(strnlen)); + ASAN_INTERCEPT_FUNC(strnlen); #endif - CHECK(INTERCEPT_FUNCTION(atoi)); - CHECK(INTERCEPT_FUNCTION(atol)); - CHECK(INTERCEPT_FUNCTION(strtol)); + ASAN_INTERCEPT_FUNC(atoi); + ASAN_INTERCEPT_FUNC(atol); + ASAN_INTERCEPT_FUNC(strtol); #if ASAN_INTERCEPT_ATOLL_AND_STRTOLL - CHECK(INTERCEPT_FUNCTION(atoll)); - CHECK(INTERCEPT_FUNCTION(strtoll)); + ASAN_INTERCEPT_FUNC(atoll); + ASAN_INTERCEPT_FUNC(strtoll); #endif // Intecept signal- and jump-related functions. - CHECK(INTERCEPT_FUNCTION(longjmp)); + ASAN_INTERCEPT_FUNC(longjmp); #if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION - CHECK(INTERCEPT_FUNCTION(sigaction)); - CHECK(INTERCEPT_FUNCTION(signal)); + ASAN_INTERCEPT_FUNC(sigaction); + ASAN_INTERCEPT_FUNC(signal); #endif #if !defined(_WIN32) - CHECK(INTERCEPT_FUNCTION(_longjmp)); + ASAN_INTERCEPT_FUNC(_longjmp); INTERCEPT_FUNCTION(__cxa_throw); # if !defined(__APPLE__) // On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it // there. - CHECK(INTERCEPT_FUNCTION(siglongjmp)); + ASAN_INTERCEPT_FUNC(siglongjmp); # endif #endif // Intercept threading-related functions #if !defined(_WIN32) - CHECK(INTERCEPT_FUNCTION(pthread_create)); + ASAN_INTERCEPT_FUNC(pthread_create); #endif // Some Windows-specific interceptors. diff --git a/compiler-rt/lib/asan/output_tests/heap-overflow.cc b/compiler-rt/lib/asan/output_tests/heap-overflow.cc index a8656c61..57233e8 100644 --- a/compiler-rt/lib/asan/output_tests/heap-overflow.cc +++ b/compiler-rt/lib/asan/output_tests/heap-overflow.cc @@ -13,7 +13,7 @@ int main(int argc, char **argv) { // Check-Common: {{0x.* is located 0 bytes to the right of 10-byte region}} // Check-Common: {{allocated by thread T0 here:}} -// Check-Linux: {{ #0 0x.* in malloc}} +// Check-Linux: {{ #0 0x.* in __xsan_malloc}} // Check-Linux: {{ #1 0x.* in main .*heap-overflow.cc:[45]}} // Check-Darwin: {{ #0 0x.* in .*mz_malloc.*}} diff --git a/compiler-rt/lib/asan/output_tests/interception_failure_test-linux.cc b/compiler-rt/lib/asan/output_tests/interception_failure_test-linux.cc new file mode 100644 index 0000000..9e8b753 --- /dev/null +++ b/compiler-rt/lib/asan/output_tests/interception_failure_test-linux.cc @@ -0,0 +1,17 @@ +#include +#include + +extern "C" long strtol(const char *nptr, char **endptr, int base) { + fprintf(stderr, "my_strtol_interceptor\n"); + return 0; +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); +} + +// Check-Common: my_strtol_interceptor +// CHECK-NOT: heap-use-after-free + diff --git a/compiler-rt/lib/asan/output_tests/interception_malloc_test-linux.cc b/compiler-rt/lib/asan/output_tests/interception_malloc_test-linux.cc new file mode 100644 index 0000000..e1a1960 --- /dev/null +++ b/compiler-rt/lib/asan/output_tests/interception_malloc_test-linux.cc @@ -0,0 +1,19 @@ +#include +#include +#include + +extern "C" void *__xsan_malloc(size_t size); +extern "C" void *malloc(size_t size) { + write(2, "malloc call\n", sizeof("malloc call\n") - 1); + return __xsan_malloc(size); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); +} + +// Check-Common: malloc call +// Check-Common: heap-use-after-free + diff --git a/compiler-rt/lib/asan/output_tests/interception_test-linux.cc b/compiler-rt/lib/asan/output_tests/interception_test-linux.cc new file mode 100644 index 0000000..84636f8 --- /dev/null +++ b/compiler-rt/lib/asan/output_tests/interception_test-linux.cc @@ -0,0 +1,18 @@ +#include +#include + +extern "C" long __xsan_strtol(const char *nptr, char **endptr, int base); +extern "C" long strtol(const char *nptr, char **endptr, int base) { + fprintf(stderr, "my_strtol_interceptor\n"); + return __xsan_strtol(nptr, endptr, base); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); +} + +// Check-Common: my_strtol_interceptor +// Check-Common: heap-use-after-free + diff --git a/compiler-rt/lib/asan/output_tests/strncpy-overflow.cc b/compiler-rt/lib/asan/output_tests/strncpy-overflow.cc index 2346188..8b72d0f 100644 --- a/compiler-rt/lib/asan/output_tests/strncpy-overflow.cc +++ b/compiler-rt/lib/asan/output_tests/strncpy-overflow.cc @@ -9,13 +9,13 @@ int main(int argc, char **argv) { } // Check-Common: {{WRITE of size 1 at 0x.* thread T0}} -// Check-Linux: {{ #0 0x.* in strncpy}} +// Check-Linux: {{ #0 0x.* in __xsan_strncpy}} // Check-Darwin: {{ #0 0x.* in wrap_strncpy}} // Check-Common: {{ #1 0x.* in main .*strncpy-overflow.cc:[78]}} // Check-Common: {{0x.* is located 0 bytes to the right of 9-byte region}} // Check-Common: {{allocated by thread T0 here:}} -// Check-Linux: {{ #0 0x.* in malloc}} +// Check-Linux: {{ #0 0x.* in __xsan_malloc}} // Check-Linux: {{ #1 0x.* in main .*strncpy-overflow.cc:6}} // Check-Darwin: {{ #0 0x.* in .*mz_malloc.*}} diff --git a/compiler-rt/lib/asan/output_tests/test_output.sh b/compiler-rt/lib/asan/output_tests/test_output.sh index ca8beb5..216760d 100755 --- a/compiler-rt/lib/asan/output_tests/test_output.sh +++ b/compiler-rt/lib/asan/output_tests/test_output.sh @@ -14,7 +14,7 @@ run_program() { ./$1 2>&1 | $SYMBOLIZER 2> /dev/null | c++filt > $TMP_ASAN_REPORT } -# check_program exe_file source_file check_prefix +# check_program exe_file source_file check_prefixf check_program() { run_program $1 $FILE_CHECK $2 --check-prefix=$3 < $TMP_ASAN_REPORT @@ -43,10 +43,14 @@ for t in *.cc; do for b in 32 64; do for O in 0 1 2 3; do c=`basename $t .cc` - if [[ "$c" == *"-so" ]] - then + if [[ "$c" == *"-so" ]]; then continue fi + if [[ "$c" == *"-linux" ]]; then + if [[ "$OS" != "Linux" ]]; then + continue + fi + fi c_so=$c-so exe=$c.$b.O$O so=$c.$b.O$O-so.so diff --git a/compiler-rt/lib/asan/output_tests/use-after-free.cc b/compiler-rt/lib/asan/output_tests/use-after-free.cc index a782d62..5d6ab5f 100644 --- a/compiler-rt/lib/asan/output_tests/use-after-free.cc +++ b/compiler-rt/lib/asan/output_tests/use-after-free.cc @@ -12,7 +12,7 @@ int main() { // Check-Common: {{0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*}} // Check-Common: {{freed by thread T0 here:}} -// Check-Linux: {{ #0 0x.* in free}} +// Check-Linux: {{ #0 0x.* in __xsan_free}} // Check-Linux: {{ #1 0x.* in main .*use-after-free.cc:[45]}} // Check-Darwin: {{ #0 0x.* in .*mz_free.*}} @@ -22,7 +22,7 @@ int main() { // Check-Common: {{previously allocated by thread T0 here:}} -// Check-Linux: {{ #0 0x.* in malloc}} +// Check-Linux: {{ #0 0x.* in __xsan_malloc}} // Check-Linux: {{ #1 0x.* in main .*use-after-free.cc:3}} // Check-Darwin: {{ #0 0x.* in .*mz_malloc.*}} diff --git a/compiler-rt/lib/interception/interception.h b/compiler-rt/lib/interception/interception.h index fb3eef0..0a7eedc 100644 --- a/compiler-rt/lib/interception/interception.h +++ b/compiler-rt/lib/interception/interception.h @@ -61,20 +61,26 @@ // source file (to define a pointer to overriden function). // How it works: -// To replace weak system functions on Linux we just need to declare functions +// To replace system functions on Linux we just need to declare functions // with same names in our library and then obtain the real function pointers -// using dlsym(). This is not so on Mac OS, where the two-level namespace makes +// using dlsym(). +// There is one complication. A user may also intercept some of the functions +// we intercept. To resolve this we declare our interceptors with __xsan_ +// prefix, and then make actual interceptors weak aliases to __xsan_ +// functions. +// This is not so on Mac OS, where the two-level namespace makes // our replacement functions invisible to other libraries. This may be overcomed // using the DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared -// libraries in Chromium were noticed when doing so. -// Instead we use mach_override, a handy framework for patching functions at -// runtime. To avoid possible name clashes, our replacement functions have +// libraries in Chromium were noticed when doing so. Instead we use +// mach_override, a handy framework for patching functions at runtime. +// To avoid possible name clashes, our replacement functions have // the "wrap_" prefix on Mac. #if defined(__APPLE__) # define WRAP(x) wrap_##x # define WRAPPER_NAME(x) "wrap_"#x # define INTERCEPTOR_ATTRIBUTE +# define DECLARE_WRAPPER(ret_type, convention, func, ...) #elif defined(_WIN32) # if defined(_DLL) // DLL CRT # define WRAP(x) x @@ -85,10 +91,14 @@ # define WRAPPER_NAME(x) "wrap_"#x # define INTERCEPTOR_ATTRIBUTE # endif +# define DECLARE_WRAPPER(ret_type, convention, func, ...) #else -# define WRAP(x) x -# define WRAPPER_NAME(x) #x +# define WRAP(x) __xsan_ ## x +# define WRAPPER_NAME(x) "__xsan_" #x # define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default"))) +# define DECLARE_WRAPPER(ret_type, convention, func, ...) \ + extern "C" ret_type convention func(__VA_ARGS__) \ + __attribute__((weak, alias("__xsan_" #func), visibility("default"))) #endif #define PTR_TO_REAL(x) real_##x @@ -125,6 +135,7 @@ #define INTERCEPTOR_EX(ret_type, convention, func, ...) \ DEFINE_REAL_EX(ret_type, convention, func, __VA_ARGS__); \ + DECLARE_WRAPPER(ret_type, convention, func, __VA_ARGS__); \ extern "C" \ INTERCEPTOR_ATTRIBUTE \ ret_type convention WRAP(func)(__VA_ARGS__) diff --git a/compiler-rt/lib/interception/interception_linux.cc b/compiler-rt/lib/interception/interception_linux.cc index 74fcf56..37e5933 100644 --- a/compiler-rt/lib/interception/interception_linux.cc +++ b/compiler-rt/lib/interception/interception_linux.cc @@ -18,9 +18,10 @@ #include // for dlsym namespace __interception { -bool GetRealFunctionAddress(const char *func_name, void **func_addr) { +bool GetRealFunctionAddress(const char *func_name, void **func_addr, + void *real, void *wrapper) { *func_addr = dlsym(RTLD_NEXT, func_name); - return (*func_addr != NULL); + return real == wrapper; } } // namespace __interception diff --git a/compiler-rt/lib/interception/interception_linux.h b/compiler-rt/lib/interception/interception_linux.h index 102b3c1..76a29c6 100644 --- a/compiler-rt/lib/interception/interception_linux.h +++ b/compiler-rt/lib/interception/interception_linux.h @@ -23,11 +23,13 @@ namespace __interception { // returns true if a function with the given name was found. -bool GetRealFunctionAddress(const char *func_name, void **func_addr); +bool GetRealFunctionAddress(const char *func_name, void **func_addr, + void *real, void *wrapper); } // namespace __interception #define INTERCEPT_FUNCTION_LINUX(func) \ - ::__interception::GetRealFunctionAddress(#func, (void**)&REAL(func)) + ::__interception::GetRealFunctionAddress(#func, (void**)&REAL(func), \ + (void*)&(func), (void*)&WRAP(func)) #endif // INTERCEPTION_LINUX_H #endif // __linux__ diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc index bc3a016..2986440 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc @@ -32,7 +32,7 @@ bool WEAK OnReport(const ReportDesc *rep, bool suppressed) { static void StackStripMain(ReportStack *stack) { ReportStack *last_frame = 0; ReportStack *last_frame2 = 0; - const char *prefix = "interception_wrap_"; + const char *prefix = "__xsan_"; uptr prefix_len = internal_strlen(prefix); const char *path_prefix = flags()->strip_path_prefix; uptr path_prefix_len = internal_strlen(path_prefix); -- 2.7.4