[compiler-rt] r209427 - [ASan/Win] Use the new function interception approach to handle function wrappers too; wrap strlen() in DLLs

Timur Iskhodzhanov timurrrr at google.com
Thu May 22 05:03:40 PDT 2014


Author: timurrrr
Date: Thu May 22 07:03:40 2014
New Revision: 209427

URL: http://llvm.org/viewvc/llvm-project?rev=209427&view=rev
Log:
[ASan/Win] Use the new function interception approach to handle function wrappers too; wrap strlen() in DLLs

Reviewed at http://reviews.llvm.org/D3871

Added:
    compiler-rt/trunk/test/asan/TestCases/Windows/dll_intercept_strlen.cc
Modified:
    compiler-rt/trunk/lib/asan/asan_dll_thunk.cc
    compiler-rt/trunk/lib/interception/interception.h
    compiler-rt/trunk/test/asan/TestCases/Windows/dll_malloc_left_oob.cc
    compiler-rt/trunk/test/asan/TestCases/Windows/dll_malloc_uaf.cc

Modified: compiler-rt/trunk/lib/asan/asan_dll_thunk.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_dll_thunk.cc?rev=209427&r1=209426&r2=209427&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_dll_thunk.cc (original)
+++ compiler-rt/trunk/lib/asan/asan_dll_thunk.cc Thu May 22 07:03:40 2014
@@ -22,7 +22,7 @@
 #ifdef ASAN_DLL_THUNK
 #include "sanitizer_common/sanitizer_interception.h"
 
-// ----------------- Helper functions and macros --------------------- {{{1
+// ---------- Function interception helper functions and macros ----------- {{{1
 extern "C" {
 void *__stdcall GetModuleHandleA(const char *module_name);
 void *__stdcall GetProcAddress(void *module, const char *proc_name);
@@ -36,68 +36,125 @@ static void *getRealProcAddressOrDie(con
   return ret;
 }
 
+// We need to intercept some functions (e.g. ASan interface, memory allocator --
+// let's call them "hooks") exported by the DLL thunk and forward the hooks to
+// the runtime in the main module.
+// However, we don't want to keep two lists of these hooks.
+// To avoid that, the list of hooks should be defined using the
+// INTERCEPT_WHEN_POSSIBLE macro. Then, all these hooks can be intercepted
+// at once by calling INTERCEPT_HOOKS().
+
+// Use macro+template magic to automatically generate the list of hooks.
+// Each hook at line LINE defines a template class with a static
+// FunctionInterceptor<LINE>::Execute() method intercepting the hook.
+// The default implementation of FunctionInterceptor<LINE> is to call
+// the Execute() method corresponding to the previous line.
+template<int LINE>
+struct FunctionInterceptor {
+  static void Execute() { FunctionInterceptor<LINE-1>::Execute(); }
+};
+
+// There shouldn't be any hooks with negative definition line number.
+template<>
+struct FunctionInterceptor<0> {
+  static void Execute() {}
+};
+
+#define INTERCEPT_WHEN_POSSIBLE(main_function, dll_function)                   \
+  template<> struct FunctionInterceptor<__LINE__> {                            \
+    static void Execute() {                                                    \
+      void *wrapper = getRealProcAddressOrDie(main_function);                  \
+      if (!__interception::OverrideFunction((uptr)dll_function,                \
+                                            (uptr)wrapper, 0))                 \
+        abort();                                                               \
+      FunctionInterceptor<__LINE__-1>::Execute();                              \
+    }                                                                          \
+  };
+
+// Special case of hooks -- ASan own interface functions.  Those are only called
+// after __asan_init, thus an empty implementation is sufficient.
+#define INTERFACE_FUNCTION(name)                                               \
+  extern "C" void name() { __debugbreak(); }                                   \
+  INTERCEPT_WHEN_POSSIBLE(#name, name)
+
+// INTERCEPT_HOOKS must be used after the last INTERCEPT_WHEN_POSSIBLE.
+#define INTERCEPT_HOOKS FunctionInterceptor<__LINE__>::Execute
+
+static void InterceptHooks();
+// }}}
+
+// ---------- Function wrapping helpers ----------------------------------- {{{1
 #define WRAP_V_V(name)                                                         \
   extern "C" void name() {                                                     \
     typedef void (*fntype)();                                                  \
     static fntype fn = (fntype)getRealProcAddressOrDie(#name);                 \
     fn();                                                                      \
-  }
+  }                                                                            \
+  INTERCEPT_WHEN_POSSIBLE(#name, name);
 
 #define WRAP_V_W(name)                                                         \
   extern "C" void name(void *arg) {                                            \
     typedef void (*fntype)(void *arg);                                         \
     static fntype fn = (fntype)getRealProcAddressOrDie(#name);                 \
     fn(arg);                                                                   \
-  }
+  }                                                                            \
+  INTERCEPT_WHEN_POSSIBLE(#name, name);
 
 #define WRAP_V_WW(name)                                                        \
   extern "C" void name(void *arg1, void *arg2) {                               \
     typedef void (*fntype)(void *, void *);                                    \
     static fntype fn = (fntype)getRealProcAddressOrDie(#name);                 \
     fn(arg1, arg2);                                                            \
-  }
+  }                                                                            \
+  INTERCEPT_WHEN_POSSIBLE(#name, name);
 
 #define WRAP_V_WWW(name)                                                       \
   extern "C" void name(void *arg1, void *arg2, void *arg3) {                   \
     typedef void *(*fntype)(void *, void *, void *);                           \
     static fntype fn = (fntype)getRealProcAddressOrDie(#name);                 \
     fn(arg1, arg2, arg3);                                                      \
-  }
+  }                                                                            \
+  INTERCEPT_WHEN_POSSIBLE(#name, name);
 
 #define WRAP_W_V(name)                                                         \
   extern "C" void *name() {                                                    \
     typedef void *(*fntype)();                                                 \
     static fntype fn = (fntype)getRealProcAddressOrDie(#name);                 \
     return fn();                                                               \
-  }
+  }                                                                            \
+  INTERCEPT_WHEN_POSSIBLE(#name, name);
 
 #define WRAP_W_W(name)                                                         \
   extern "C" void *name(void *arg) {                                           \
     typedef void *(*fntype)(void *arg);                                        \
     static fntype fn = (fntype)getRealProcAddressOrDie(#name);                 \
     return fn(arg);                                                            \
-  }
+  }                                                                            \
+  INTERCEPT_WHEN_POSSIBLE(#name, name);
 
 #define WRAP_W_WW(name)                                                        \
   extern "C" void *name(void *arg1, void *arg2) {                              \
     typedef void *(*fntype)(void *, void *);                                   \
     static fntype fn = (fntype)getRealProcAddressOrDie(#name);                 \
     return fn(arg1, arg2);                                                     \
-  }
+  }                                                                            \
+  INTERCEPT_WHEN_POSSIBLE(#name, name);
 
 #define WRAP_W_WWW(name)                                                       \
   extern "C" void *name(void *arg1, void *arg2, void *arg3) {                  \
     typedef void *(*fntype)(void *, void *, void *);                           \
     static fntype fn = (fntype)getRealProcAddressOrDie(#name);                 \
     return fn(arg1, arg2, arg3);                                               \
-  }
+  }                                                                            \
+  INTERCEPT_WHEN_POSSIBLE(#name, name);
 
 #define WRAP_W_WWWW(name)                                                      \
   extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) {      \
     typedef void *(*fntype)(void *, void *, void *, void *);                   \
     static fntype fn = (fntype)getRealProcAddressOrDie(#name);                 \
     return fn(arg1, arg2, arg3, arg4);                                         \
-  }
+  }                                                                            \
+  INTERCEPT_WHEN_POSSIBLE(#name, name);
 
 #define WRAP_W_WWWWW(name)                                                     \
   extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4,        \
@@ -105,7 +162,8 @@ static void *getRealProcAddressOrDie(con
     typedef void *(*fntype)(void *, void *, void *, void *, void *);           \
     static fntype fn = (fntype)getRealProcAddressOrDie(#name);                 \
     return fn(arg1, arg2, arg3, arg4, arg5);                                   \
-  }
+  }                                                                            \
+  INTERCEPT_WHEN_POSSIBLE(#name, name);
 
 #define WRAP_W_WWWWWW(name)                                                    \
   extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4,        \
@@ -113,48 +171,8 @@ static void *getRealProcAddressOrDie(con
     typedef void *(*fntype)(void *, void *, void *, void *, void *, void *);   \
     static fntype fn = (fntype)getRealProcAddressOrDie(#name);                 \
     return fn(arg1, arg2, arg3, arg4, arg5, arg6);                             \
-  }
-// }}}
-
-// --------- Interface interception helper functions and macros ----------- {{{1
-// We need to intercept the ASan interface exported by the DLL thunk and forward
-// all the functions to the runtime in the main module.
-// However, we don't want to keep two lists of interface functions.
-// To avoid that, the list of interface functions should be defined using the
-// INTERFACE_FUNCTION macro. Then, all the interface can be intercepted at once
-// by calling INTERCEPT_ASAN_INTERFACE().
-
-// Use macro+template magic to automatically generate the list of interface
-// functions.  Each interface function at line LINE defines a template class
-// with a static InterfaceInteceptor<LINE>::Execute() method intercepting the
-// function.  The default implementation of InterfaceInteceptor<LINE> is to call
-// the Execute() method corresponding to the previous line.
-template<int LINE>
-struct InterfaceInteceptor {
-  static void Execute() { InterfaceInteceptor<LINE-1>::Execute(); }
-};
-
-// There shouldn't be any interface function with negative line number.
-template<>
-struct InterfaceInteceptor<0> {
-  static void Execute() {}
-};
-
-#define INTERFACE_FUNCTION(name)                                               \
-  extern "C" void name() { __debugbreak(); }                                   \
-  template<> struct InterfaceInteceptor<__LINE__> {                            \
-    static void Execute() {                                                    \
-      void *wrapper = getRealProcAddressOrDie(#name);                          \
-      if (!__interception::OverrideFunction((uptr)name, (uptr)wrapper, 0))     \
-        abort();                                                               \
-      InterfaceInteceptor<__LINE__-1>::Execute();                              \
-    }                                                                          \
-  };
-
-// INTERCEPT_ASAN_INTERFACE must be used after the last INTERFACE_FUNCTION.
-#define INTERCEPT_ASAN_INTERFACE InterfaceInteceptor<__LINE__>::Execute
-
-static void InterceptASanInterface();
+  }                                                                            \
+  INTERCEPT_WHEN_POSSIBLE(#name, name);
 // }}}
 
 // ----------------- ASan own interface functions --------------------
@@ -178,7 +196,7 @@ extern "C" {
     __asan_option_detect_stack_use_after_return =
         (__asan_should_detect_stack_use_after_return() != 0);
 
-    InterceptASanInterface();
+    InterceptHooks();
   }
 }
 
@@ -266,8 +284,14 @@ WRAP_W_W(_expand_dbg)
 
 // TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cc).
 
-void InterceptASanInterface() {
-  INTERCEPT_ASAN_INTERFACE();
+// strlen is an intrinsic function, so we must specify its exact return and
+// parameter types to avoid a compiler error.
+extern "C" unsigned strlen(const char *s);
+INTERCEPT_WHEN_POSSIBLE(WRAPPER_NAME(strlen), strlen);
+
+// Must be at the end of the file due to the way INTERCEPT_HOOKS is defined.
+void InterceptHooks() {
+  INTERCEPT_HOOKS();
 }
 
 #endif // ASAN_DLL_THUNK

Modified: compiler-rt/trunk/lib/interception/interception.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/interception/interception.h?rev=209427&r1=209426&r2=209427&view=diff
==============================================================================
--- compiler-rt/trunk/lib/interception/interception.h (original)
+++ compiler-rt/trunk/lib/interception/interception.h Thu May 22 07:03:40 2014
@@ -127,9 +127,9 @@ const interpose_substitution substitutio
 #  define WRAPPER_NAME(x) #x
 #  define INTERCEPTOR_ATTRIBUTE
 # else  // Static CRT
-#  define WRAP(x) wrap_##x
-#  define WRAPPER_NAME(x) "wrap_"#x
-#  define INTERCEPTOR_ATTRIBUTE
+#  define WRAP(x) __asan_wrap_##x
+#  define WRAPPER_NAME(x) "__asan_wrap_"#x
+#  define INTERCEPTOR_ATTRIBUTE __declspec(dllexport)
 # endif
 # define DECLARE_WRAPPER(ret_type, func, ...) \
     extern "C" ret_type func(__VA_ARGS__);

Added: compiler-rt/trunk/test/asan/TestCases/Windows/dll_intercept_strlen.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Windows/dll_intercept_strlen.cc?rev=209427&view=auto
==============================================================================
--- compiler-rt/trunk/test/asan/TestCases/Windows/dll_intercept_strlen.cc (added)
+++ compiler-rt/trunk/test/asan/TestCases/Windows/dll_intercept_strlen.cc Thu May 22 07:03:40 2014
@@ -0,0 +1,29 @@
+// RUN: %clangxx_asan -O0 %p/dll_host.cc -Fe%t
+// RUN: %clangxx_asan -LD -O0 %s -Fe%t.dll
+// FIXME: 'cat' is needed due to PR19744.
+// RUN: not %run %t %t.dll 2>&1 | cat | FileCheck %s
+
+#include <stdio.h>
+#include <string.h>
+
+extern "C" __declspec(dllexport)
+int test_function() {
+  char str[] = "Hello!";
+  if (6 != strlen(str))
+    return 1;
+  printf("Initial test OK\n");
+  fflush(0);
+// CHECK: Initial test OK
+
+  str[6] = '!';  // Removes '\0' at the end!
+  int len = strlen(str);
+// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]]
+// FIXME: Should be READ of size 1, see issue 155.
+// CHECK: READ of size {{[0-9]+}} at [[ADDR]] thread T0
+// CHECK-NEXT: {{#0 .*}}strlen
+// CHECK-NEXT: {{#1 .* test_function .*}}dll_intercept_strlen.cc:[[@LINE-5]]
+//
+// CHECK: Address [[ADDR]] is located in stack of thread T0 at offset {{.*}} in frame
+// CHECK-NEXT: test_function {{.*}}dll_intercept_strlen.cc:
+  return len > 42;
+}

Modified: compiler-rt/trunk/test/asan/TestCases/Windows/dll_malloc_left_oob.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Windows/dll_malloc_left_oob.cc?rev=209427&r1=209426&r2=209427&view=diff
==============================================================================
--- compiler-rt/trunk/test/asan/TestCases/Windows/dll_malloc_left_oob.cc (original)
+++ compiler-rt/trunk/test/asan/TestCases/Windows/dll_malloc_left_oob.cc Thu May 22 07:03:40 2014
@@ -15,8 +15,8 @@ int test_function() {
 //
 // CHECK: [[ADDR]] is located 1 bytes to the left of 42-byte region
 // CHECK-LABEL: allocated by thread T0 here:
-// CHECK:        malloc
-// CHECK:        test_function {{.*}}dll_malloc_left_oob.cc:[[@LINE-10]]
+// CHECK-NEXT:   malloc
+// CHECK-NEXT:   test_function {{.*}}dll_malloc_left_oob.cc:[[@LINE-10]]
 // CHECK-NEXT:   main {{.*}}dll_host.cc
 // CHECK-LABEL: SUMMARY
   free(buffer);

Modified: compiler-rt/trunk/test/asan/TestCases/Windows/dll_malloc_uaf.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Windows/dll_malloc_uaf.cc?rev=209427&r1=209426&r2=209427&view=diff
==============================================================================
--- compiler-rt/trunk/test/asan/TestCases/Windows/dll_malloc_uaf.cc (original)
+++ compiler-rt/trunk/test/asan/TestCases/Windows/dll_malloc_uaf.cc Thu May 22 07:03:40 2014
@@ -17,13 +17,13 @@ int test_function() {
 //
 // CHECK: [[ADDR]] is located 0 bytes inside of 42-byte region
 // CHECK-LABEL: freed by thread T0 here:
-// CHECK:       free
-// CHECK:       test_function {{.*}}dll_malloc_uaf.cc:[[@LINE-10]]
+// CHECK-NEXT:  free
+// CHECK-NEXT:  test_function {{.*}}dll_malloc_uaf.cc:[[@LINE-10]]
 // CHECK-NEXT:  main {{.*}}dll_host
 //
 // CHECK-LABEL: previously allocated by thread T0 here:
-// CHECK:       malloc
-// CHECK:       test_function {{.*}}dll_malloc_uaf.cc:[[@LINE-16]]
+// CHECK-NEXT:  malloc
+// CHECK-NEXT:  test_function {{.*}}dll_malloc_uaf.cc:[[@LINE-16]]
 // CHECK-NEXT:  main {{.*}}dll_host
   return 0;
 }





More information about the llvm-commits mailing list