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
}
#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,
namespace __asan {
void InitializeWindowsInterceptors() {
- CHECK(INTERCEPT_FUNCTION(CreateThread));
+ ASAN_INTERCEPT_FUNC(CreateThread);
}
} // namespace __asan
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.
// 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.*}}
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+
+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
+
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+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
+
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+
+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
+
}
// 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.*}}
./$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
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
// 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.*}}
// 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.*}}
// 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
# 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
#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__)
#include <dlfcn.h> // 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
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__
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);