diff options
-rw-r--r-- | lib/asan/asan_interceptors.cc | 25 | ||||
-rw-r--r-- | lib/asan/asan_malloc_win.cc | 15 | ||||
-rw-r--r-- | lib/asan/asan_win.cc | 4 | ||||
-rw-r--r-- | lib/asan/interception/interception.h | 13 | ||||
-rw-r--r-- | lib/asan/interception/interception_win.cc | 113 | ||||
-rw-r--r-- | lib/asan/interception/interception_win.h | 11 |
6 files changed, 170 insertions, 11 deletions
diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc index 35214cbcf..27cd956f6 100644 --- a/lib/asan/asan_interceptors.cc +++ b/lib/asan/asan_interceptors.cc @@ -34,6 +34,31 @@ #include <strings.h> #endif // __APPLE__ +#if defined(_WIN32) && !defined(_DLL) +// FIXME: We might want to use these on Mac too. +extern "C" { +int memcmp(const void *b1, const void *b2, size_t sz); +void memmove(void *d, const void *s, size_t sz); +void memcpy(void *d, const void *s, size_t sz); +void memset(void *b, int c, size_t sz); + +char* strchr(const char *s, char c); +char* strcat(char *d, const char* s); // NOLINT +char* strncat(char *d, const char* s, size_t sz); +char* strcpy(char *d, const char* s); // NOLINT +char* strncpy(char *d, const char* s, size_t sz); +int strcmp(const char *s1, const char* s2); +int strncmp(const char *s1, const char* s2, size_t sz); +size_t strnlen(const char *s1, size_t sz); + +void longjmp(void* env, int value); + +__declspec(dllimport) +void* __stdcall CreateThread(void *sec, size_t st, void* start, + void *arg, DWORD fl, DWORD *id); +} // extern "C" +#endif + namespace __asan { // Instruments read/write access to a single byte in memory. diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc index 4e87fcac5..223561610 100644 --- a/lib/asan/asan_malloc_win.cc +++ b/lib/asan/asan_malloc_win.cc @@ -44,6 +44,11 @@ void *calloc(size_t nmemb, size_t size) { return asan_calloc(nmemb, size, &stack); } +void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_calloc(nmemb, size, &stack); +} + void *realloc(void *ptr, size_t size) { GET_STACK_TRACE_HERE_FOR_MALLOC; return asan_realloc(ptr, size, &stack); @@ -65,9 +70,10 @@ const int PAGE_EXECUTE_READWRITE = 0x40; namespace __asan { void ReplaceSystemMalloc() { -#ifdef _WIN64 -# error ReplaceSystemMalloc was not tested on x64 -#endif +#if defined(_DLL) +# ifdef _WIN64 +# error ReplaceSystemMalloc was not tested on x64 +# endif char *crt_malloc; if (GetRealFunctionAddress("malloc", (void**)&crt_malloc)) { // Replace malloc in the CRT dll with a jump to our malloc. @@ -75,7 +81,7 @@ void ReplaceSystemMalloc() { CHECK(VirtualProtect(crt_malloc, 16, PAGE_EXECUTE_READWRITE, &old_prot)); REAL(memset)(crt_malloc, 0xCC /* int 3 */, 16); // just in case. - uintptr_t jmp_offset = (intptr_t)malloc - (intptr_t)crt_malloc - 5; + ptrdiff_t jmp_offset = (char*)malloc - (char*)crt_malloc - 5; crt_malloc[0] = 0xE9; // jmp, should be followed by an offset. REAL(memcpy)(crt_malloc + 1, &jmp_offset, sizeof(jmp_offset)); @@ -85,6 +91,7 @@ void ReplaceSystemMalloc() { } // FIXME: investigate whether anything else is needed. +#endif } } // namespace __asan diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc index 51f32e91f..53d6777b6 100644 --- a/lib/asan/asan_win.cc +++ b/lib/asan/asan_win.cc @@ -224,8 +224,8 @@ void AsanTSDSet(void *tsd) { // ---------------------- Various stuff ---------------- {{{1 void *AsanDoesNotSupportStaticLinkage() { -#if !defined(_DLL) || defined(_DEBUG) -#error Please build the runtime with /MD +#if defined(_DEBUG) +#error Please build the runtime with a non-debug CRT: /MD or /MT #endif return NULL; } diff --git a/lib/asan/interception/interception.h b/lib/asan/interception/interception.h index 50c03bb17..fb3eef08a 100644 --- a/lib/asan/interception/interception.h +++ b/lib/asan/interception/interception.h @@ -76,10 +76,15 @@ # define WRAPPER_NAME(x) "wrap_"#x # define INTERCEPTOR_ATTRIBUTE #elif defined(_WIN32) -// TODO(timurrrr): we're likely to use something else later on Windows. -# define WRAP(x) x -# define WRAPPER_NAME(x) #x -# define INTERCEPTOR_ATTRIBUTE +# if defined(_DLL) // DLL CRT +# define WRAP(x) x +# 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 +# endif #else # define WRAP(x) x # define WRAPPER_NAME(x) #x diff --git a/lib/asan/interception/interception_win.cc b/lib/asan/interception/interception_win.cc index a3f0f2b0c..a60c741cb 100644 --- a/lib/asan/interception/interception_win.cc +++ b/lib/asan/interception/interception_win.cc @@ -31,6 +31,119 @@ bool GetRealFunctionAddress(const char *func_name, void **func_addr) { } return (*func_addr != NULL); } + +// FIXME: internal_str* and internal_mem* functions should be moved from the +// ASan sources into interception/. + +static void _memset(void *p, int value, size_t sz) { + for (size_t i = 0; i < sz; ++i) + ((char*)p)[i] = (char)value; +} + +static void _memcpy(void *dst, void *src, size_t sz) { + char *dst_c = (char*)dst, + *src_c = (char*)src; + for (size_t i = 0; i < sz; ++i) + dst_c[i] = src_c[i]; +} + +static void WriteJumpInstruction(char *jmp_from, char *to) { + // jmp XXYYZZWW = E9 WW ZZ YY XX, where XXYYZZWW is an offset fromt jmp_from + // to the next instruction to the destination. + ptrdiff_t offset = to - jmp_from - 5; + *jmp_from = '\xE9'; + *(ptrdiff_t*)(jmp_from + 1) = offset; +} + +bool OverrideFunction(void *old_func, void *new_func, void **orig_old_func) { +#ifdef _WIN64 +# error OverrideFunction was not tested on x64 +#endif + // Basic idea: + // We write 5 bytes (jmp-to-new_func) at the beginning of the 'old_func' + // to override it. We want to be able to execute the original 'old_func' from + // the wrapper, so we need to keep the leading 5+ bytes ('head') of the + // original instructions somewhere with a "jmp old_func+head". + // We call these 'head'+5 bytes of instructions a "trampoline". + + // Trampolines are allocated from a common pool. + const int POOL_SIZE = 1024; + static char *pool = NULL; + static size_t pool_used = 0; + if (pool == NULL) { + pool = (char*)VirtualAlloc(NULL, POOL_SIZE, + MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE); + // FIXME: set PAGE_EXECUTE_READ access after setting all interceptors? + if (pool == NULL) + return false; + _memset(pool, 0xCC /* int 3 */, POOL_SIZE); + } + + char* old_bytes = (char*)old_func; + char* trampoline = pool + pool_used; + + // Find out the number of bytes of the instructions we need to copy to the + // island and store it in 'head'. + size_t head = 0; + while (head < 5) { + switch (old_bytes[head]) { + case '\x55': // push ebp + case '\x56': // push esi + case '\x57': // push edi + head++; + continue; + } + switch (*(unsigned short*)(old_bytes + head)) { // NOLINT + case 0xFF8B: // 8B FF = mov edi, edi + case 0xEC8B: // 8B EC = mov ebp, esp + case 0xC033: // 33 C0 = xor eax, eax + head += 2; + continue; + case 0xEC83: // 83 EC XX = sub esp, XX + head += 3; + continue; + case 0xC1F7: // F7 C1 XX YY ZZ WW = test ecx, WWZZYYXX + head += 6; + continue; + } + switch (0x00FFFFFF & *(unsigned int*)(old_bytes + head)) { + case 0x24448A: // 8A 44 24 XX = mov eal, dword ptr [esp+XXh] + case 0x244C8B: // 8B 4C 24 XX = mov ecx, dword ptr [esp+XXh] + case 0x24548B: // 8B 54 24 XX = mov edx, dword ptr [esp+XXh] + case 0x247C8B: // 8B 7C 24 XX = mov edi, dword ptr [esp+XXh] + head += 4; + continue; + } + + // Unknown instruction! + return false; + } + + if (pool_used + head + 5 > POOL_SIZE) + return false; + + // Now put the "jump to trampoline" instruction into the original code. + DWORD old_prot, unused_prot; + if (!VirtualProtect(old_func, head, PAGE_EXECUTE_READWRITE, &old_prot)) + return false; + + // Put the needed instructions into the trampoline bytes. + _memcpy(trampoline, old_bytes, head); + WriteJumpInstruction(trampoline + head, old_bytes + head); + *orig_old_func = trampoline; + pool_used += head + 5; + + // Intercept the 'old_func'. + WriteJumpInstruction(old_bytes, (char*)new_func); + _memset(old_bytes + 5, 0xCC /* int 3 */, head - 5); + + if (!VirtualProtect(old_func, head, old_prot, &unused_prot)) + return false; // not clear if this failure bothers us. + + return true; +} + } // namespace __interception #endif // _WIN32 diff --git a/lib/asan/interception/interception_win.h b/lib/asan/interception/interception_win.h index fb7988eac..9d1586ecb 100644 --- a/lib/asan/interception/interception_win.h +++ b/lib/asan/interception/interception_win.h @@ -24,10 +24,19 @@ namespace __interception { // returns true if a function with the given name was found. bool GetRealFunctionAddress(const char *func_name, void **func_addr); + +// returns true if the old function existed, false on failure. +bool OverrideFunction(void *old_func, void *new_func, void **orig_old_func); } // namespace __interception -#define INTERCEPT_FUNCTION_WIN(func) \ +#if defined(_DLL) +# define INTERCEPT_FUNCTION_WIN(func) \ ::__interception::GetRealFunctionAddress(#func, (void**)&REAL(func)) +#else +# define INTERCEPT_FUNCTION_WIN(func) \ + ::__interception::OverrideFunction((void*)func, (void*)WRAP(func), \ + (void**)&REAL(func)) +#endif #endif // INTERCEPTION_WIN_H #endif // _WIN32 |