Skip to content

Commit 848a19e

Browse files
committed
[sanitizers][windows] Rtl-Heap Interception and tests
- Adds interceptors for Rtl[Allocate|Free|Size|ReAllocate]Heap - Adds unit tests for the new interceptors and expands HeapAlloc tests to demonstrate new functionality. Reviewed as D62927 llvm-svn: 365422
1 parent 53d5f3a commit 848a19e

25 files changed

+1081
-55
lines changed

compiler-rt/lib/asan/asan_flags.inc

+2
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,5 @@ ASAN_FLAG(bool, allocator_frees_and_returns_null_on_realloc_zero, true,
158158
ASAN_FLAG(bool, verify_asan_link_order, true,
159159
"Check position of ASan runtime in library list (needs to be disabled"
160160
" when other library has to be preloaded system-wide)")
161+
ASAN_FLAG(bool, windows_hook_rtl_allocators, false,
162+
"(Windows only) enable hooking of Rtl(Allocate|Free|Size|ReAllocate)Heap.")

compiler-rt/lib/asan/asan_malloc_win.cc

+283-26
Large diffs are not rendered by default.

compiler-rt/lib/asan/asan_win.cc

+50-25
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@
2020

2121
#include "asan_interceptors.h"
2222
#include "asan_internal.h"
23+
#include "asan_mapping.h"
2324
#include "asan_report.h"
2425
#include "asan_stack.h"
2526
#include "asan_thread.h"
26-
#include "asan_mapping.h"
2727
#include "sanitizer_common/sanitizer_libc.h"
2828
#include "sanitizer_common/sanitizer_mutex.h"
2929
#include "sanitizer_common/sanitizer_win.h"
@@ -77,7 +77,7 @@ static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
7777
}
7878

7979
INTERCEPTOR_WINAPI(LPTOP_LEVEL_EXCEPTION_FILTER, SetUnhandledExceptionFilter,
80-
LPTOP_LEVEL_EXCEPTION_FILTER ExceptionFilter) {
80+
LPTOP_LEVEL_EXCEPTION_FILTER ExceptionFilter) {
8181
CHECK(REAL(SetUnhandledExceptionFilter));
8282
if (ExceptionFilter == &SEHHandler)
8383
return REAL(SetUnhandledExceptionFilter)(ExceptionFilter);
@@ -132,7 +132,7 @@ INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
132132
#endif
133133

134134
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
135-
AsanThread *t = (AsanThread*)arg;
135+
AsanThread *t = (AsanThread *)arg;
136136
SetCurrentThread(t);
137137
return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr);
138138
}
@@ -162,10 +162,9 @@ void InitializePlatformInterceptors() {
162162
// The interceptors were not designed to be removable, so we have to keep this
163163
// module alive for the life of the process.
164164
HMODULE pinned;
165-
CHECK(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
166-
GET_MODULE_HANDLE_EX_FLAG_PIN,
167-
(LPCWSTR)&InitializePlatformInterceptors,
168-
&pinned));
165+
CHECK(GetModuleHandleExW(
166+
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN,
167+
(LPCWSTR)&InitializePlatformInterceptors, &pinned));
169168

170169
ASAN_INTERCEPT_FUNC(CreateThread);
171170
ASAN_INTERCEPT_FUNC(SetUnhandledExceptionFilter);
@@ -197,24 +196,46 @@ static bool tsd_key_inited = false;
197196

198197
static __declspec(thread) void *fake_tsd = 0;
199198

199+
// https://docs.microsoft.com/en-us/windows/desktop/api/winternl/ns-winternl-_teb
200+
// "[This structure may be altered in future versions of Windows. Applications
201+
// should use the alternate functions listed in this topic.]"
202+
typedef struct _TEB {
203+
PVOID Reserved1[12];
204+
// PVOID ThreadLocalStoragePointer; is here, at the last field in Reserved1.
205+
PVOID ProcessEnvironmentBlock;
206+
PVOID Reserved2[399];
207+
BYTE Reserved3[1952];
208+
PVOID TlsSlots[64];
209+
BYTE Reserved4[8];
210+
PVOID Reserved5[26];
211+
PVOID ReservedForOle;
212+
PVOID Reserved6[4];
213+
PVOID TlsExpansionSlots;
214+
} TEB, *PTEB;
215+
216+
constexpr size_t TEB_RESERVED_FIELDS_THREAD_LOCAL_STORAGE_OFFSET = 11;
217+
BOOL IsTlsInitialized() {
218+
PTEB teb = (PTEB)NtCurrentTeb();
219+
return teb->Reserved1[TEB_RESERVED_FIELDS_THREAD_LOCAL_STORAGE_OFFSET] !=
220+
nullptr;
221+
}
222+
200223
void AsanTSDInit(void (*destructor)(void *tsd)) {
201224
// FIXME: we're ignoring the destructor for now.
202225
tsd_key_inited = true;
203226
}
204227

205228
void *AsanTSDGet() {
206229
CHECK(tsd_key_inited);
207-
return fake_tsd;
230+
return IsTlsInitialized() ? fake_tsd : nullptr;
208231
}
209232

210233
void AsanTSDSet(void *tsd) {
211234
CHECK(tsd_key_inited);
212235
fake_tsd = tsd;
213236
}
214237

215-
void PlatformTSDDtor(void *tsd) {
216-
AsanThread::TSDDtor(tsd);
217-
}
238+
void PlatformTSDDtor(void *tsd) { AsanThread::TSDDtor(tsd); }
218239
// }}}
219240

220241
// ---------------------- Various stuff ---------------- {{{
@@ -245,9 +266,7 @@ void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
245266
UNIMPLEMENTED();
246267
}
247268

248-
void AsanOnDeadlySignal(int, void *siginfo, void *context) {
249-
UNIMPLEMENTED();
250-
}
269+
void AsanOnDeadlySignal(int, void *siginfo, void *context) { UNIMPLEMENTED(); }
251270

252271
#if SANITIZER_WINDOWS64
253272
// Exception handler for dealing with shadow memory.
@@ -256,7 +275,9 @@ ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) {
256275
uptr page_size = GetPageSizeCached();
257276
// Only handle access violations.
258277
if (exception_pointers->ExceptionRecord->ExceptionCode !=
259-
EXCEPTION_ACCESS_VIOLATION) {
278+
EXCEPTION_ACCESS_VIOLATION ||
279+
exception_pointers->ExceptionRecord->NumberParameters < 2) {
280+
__asan_handle_no_return();
260281
return EXCEPTION_CONTINUE_SEARCH;
261282
}
262283

@@ -265,7 +286,10 @@ ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) {
265286
(uptr)(exception_pointers->ExceptionRecord->ExceptionInformation[1]);
266287

267288
// Check valid shadow range.
268-
if (!AddrIsInShadow(addr)) return EXCEPTION_CONTINUE_SEARCH;
289+
if (!AddrIsInShadow(addr)) {
290+
__asan_handle_no_return();
291+
return EXCEPTION_CONTINUE_SEARCH;
292+
}
269293

270294
// This is an access violation while trying to read from the shadow. Commit
271295
// the relevant page and let execution continue.
@@ -276,7 +300,8 @@ ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) {
276300
// Commit the page.
277301
uptr result =
278302
(uptr)::VirtualAlloc((LPVOID)page, page_size, MEM_COMMIT, PAGE_READWRITE);
279-
if (result != page) return EXCEPTION_CONTINUE_SEARCH;
303+
if (result != page)
304+
return EXCEPTION_CONTINUE_SEARCH;
280305

281306
// The page mapping succeeded, so continue execution as usual.
282307
return EXCEPTION_CONTINUE_EXECUTION;
@@ -293,7 +318,7 @@ void InitializePlatformExceptionHandlers() {
293318
}
294319

295320
bool IsSystemHeapAddress(uptr addr) {
296-
return ::HeapValidate(GetProcessHeap(), 0, (void*)addr) != FALSE;
321+
return ::HeapValidate(GetProcessHeap(), 0, (void *)addr) != FALSE;
297322
}
298323

299324
// We want to install our own exception handler (EH) to print helpful reports
@@ -312,8 +337,7 @@ bool IsSystemHeapAddress(uptr addr) {
312337
// asan_dynamic_runtime_thunk.lib to all the modules, thus __asan_set_seh_filter
313338
// will be called for each instrumented module. This ensures that at least one
314339
// __asan_set_seh_filter call happens after the .exe module CRT is initialized.
315-
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
316-
int __asan_set_seh_filter() {
340+
extern "C" SANITIZER_INTERFACE_ATTRIBUTE int __asan_set_seh_filter() {
317341
// We should only store the previous handler if it's not our own handler in
318342
// order to avoid loops in the EH chain.
319343
auto prev_seh_handler = SetUnhandledExceptionFilter(SEHHandler);
@@ -347,12 +371,13 @@ __declspec(allocate(".CRT$XCAB")) int (*__intercept_seh)() =
347371
// which run before the CRT. Users also add code to .CRT$XLC, so it's important
348372
// to run our initializers first.
349373
static void NTAPI asan_thread_init(void *module, DWORD reason, void *reserved) {
350-
if (reason == DLL_PROCESS_ATTACH) __asan_init();
374+
if (reason == DLL_PROCESS_ATTACH)
375+
__asan_init();
351376
}
352377

353378
#pragma section(".CRT$XLAB", long, read) // NOLINT
354-
__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *,
355-
unsigned long, void *) = asan_thread_init;
379+
__declspec(allocate(".CRT$XLAB")) void(NTAPI *__asan_tls_init)(
380+
void *, unsigned long, void *) = asan_thread_init;
356381
#endif
357382

358383
static void NTAPI asan_thread_exit(void *module, DWORD reason, void *reserved) {
@@ -365,8 +390,8 @@ static void NTAPI asan_thread_exit(void *module, DWORD reason, void *reserved) {
365390
}
366391

367392
#pragma section(".CRT$XLY", long, read) // NOLINT
368-
__declspec(allocate(".CRT$XLY")) void (NTAPI *__asan_tls_exit)(void *,
369-
unsigned long, void *) = asan_thread_exit;
393+
__declspec(allocate(".CRT$XLY")) void(NTAPI *__asan_tls_exit)(
394+
void *, unsigned long, void *) = asan_thread_exit;
370395

371396
WIN_FORCE_LINK(__asan_dso_reg_hook)
372397

compiler-rt/test/asan/TestCases/Windows/dll_host.cc

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
// IMPORT: __asan_wrap_RaiseException
3333
// IMPORT: __asan_wrap_RtlRaiseException
3434
// IMPORT: __asan_wrap_SetUnhandledExceptionFilter
35+
// IMPORT: __asan_wrap_RtlSizeHeap
36+
// IMPORT: __asan_wrap_RtlAllocateHeap
37+
// IMPORT: __asan_wrap_RtlReAllocateHeap
38+
// IMPORT: __asan_wrap_RtlFreeHeap
3539
//
3640
// RUN: cat %t.imports1 %t.imports2 | sort | uniq > %t.imports-sorted
3741
// RUN: cat %t.exports1 %t.exports2 | sort | uniq > %t.exports-sorted
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include <stdio.h>
2+
#include <windows.h>
3+
4+
// RUN: %clang_cl_asan -LD /Od -DDLL %s -Fe%t.dll
5+
// RUN: %clang_cl /Od -DEXE %s -Fe%te.exe
6+
// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %te.exe %t.dll 2>&1 | FileCheck %s
7+
// REQUIRES: asan-dynamic-runtime
8+
// REQUIRES: asan-32-bits
9+
10+
#include <cassert>
11+
#include <stdio.h>
12+
#include <windows.h>
13+
extern "C" {
14+
#if defined(EXE)
15+
16+
int main(int argc, char **argv) {
17+
void *region_without_hooks = HeapAlloc(GetProcessHeap(), 0, 10);
18+
HMODULE lib = LoadLibraryA(argv[1]);
19+
assert(lib != INVALID_HANDLE_VALUE);
20+
21+
void *region_w_hooks = HeapAlloc(GetProcessHeap(), 0, 10);
22+
assert(region_w_hooks != nullptr);
23+
assert(0 != FreeLibrary(lib));
24+
25+
fprintf(stderr, "WITHOUT:0x%08x\n", (unsigned int)region_without_hooks);
26+
fprintf(stderr, "WITH:0x%08x\n", (unsigned int)region_w_hooks);
27+
28+
assert(0 != HeapFree(GetProcessHeap(), 0, region_without_hooks));
29+
assert(0 != HeapFree(GetProcessHeap(), 0, region_w_hooks));
30+
31+
HeapFree(GetProcessHeap(), 0, region_w_hooks); //will dump
32+
}
33+
#elif defined(DLL)
34+
// This global is registered at startup.
35+
36+
BOOL WINAPI DllMain(HMODULE, DWORD reason, LPVOID) {
37+
fprintf(stderr, "in DLL(reason=%d)\n", (int)reason);
38+
fflush(0);
39+
return TRUE;
40+
}
41+
42+
// CHECK: in DLL(reason=1)
43+
// CHECK: in DLL(reason=0)
44+
// CHECK: WITHOUT:[[WITHOUT:0x[0-9a-fA-F]+]]
45+
// CHECK: WITH:[[WITH:0x[0-9a-fA-F]+]]
46+
// CHECK: AddressSanitizer: attempting double-free on [[WITH]] in thread T0:
47+
48+
#else
49+
#error oops!
50+
#endif
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// XFAIL: asan-64-bits
2+
// RUN: %clang_cl_asan -O0 %s -Fe%t
3+
// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s
4+
5+
#include <windows.h>
6+
7+
int main() {
8+
char *buffer;
9+
buffer = (char *)HeapAlloc(GetProcessHeap(), 0, 32),
10+
buffer[33] = 'a';
11+
// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]]
12+
// CHECK: WRITE of size 1 at [[ADDR]] thread T0
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include <stdio.h>
2+
#include <windows.h>
3+
4+
// RUN: %clang_cl_asan -LD /Od -DDLL %s -Fe%t.dll
5+
// RUN: %clang_cl /Od -DEXE %s -Fe%te.exe
6+
// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %te.exe %t.dll 2>&1 | FileCheck %s
7+
// REQUIRES: asan-dynamic-runtime
8+
// REQUIRES: asan-32-bits
9+
10+
#include <cassert>
11+
#include <stdio.h>
12+
#include <windows.h>
13+
extern "C" {
14+
#if defined(EXE)
15+
16+
int main(int argc, char **argv) {
17+
void *region_without_hooks = HeapAlloc(GetProcessHeap(), 0, 10);
18+
HMODULE lib = LoadLibraryA(argv[1]);
19+
assert(lib != INVALID_HANDLE_VALUE);
20+
assert(0 != FreeLibrary(lib));
21+
assert(0 != HeapFree(GetProcessHeap(), 0, region_without_hooks));
22+
assert(0 != HeapFree(GetProcessHeap(), 0, region_without_hooks));
23+
}
24+
#elif defined(DLL)
25+
// This global is registered at startup.
26+
27+
BOOL WINAPI DllMain(HMODULE, DWORD reason, LPVOID) {
28+
fprintf(stderr, "in DLL(reason=%d)\n", (int)reason);
29+
fflush(0);
30+
return TRUE;
31+
}
32+
33+
// CHECK: in DLL(reason=1)
34+
// CHECK: in DLL(reason=0)
35+
// CHECK: AddressSanitizer: nested bug in the same thread, aborting.
36+
37+
#else
38+
#error oops!
39+
#endif
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include <stdio.h>
2+
#include <windows.h>
3+
4+
// RUN: %clang_cl_asan -LD /Od -DDLL %s -Fe%t.dll
5+
// RUN: %clang_cl /Od -DEXE %s -Fe%te.exe
6+
// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %te.exe %t.dll 2>&1 | FileCheck %s
7+
// REQUIRES: asan-dynamic-runtime
8+
// REQUIRES: asan-32-bits
9+
10+
#include <cassert>
11+
#include <stdio.h>
12+
#include <windows.h>
13+
extern "C" {
14+
#if defined(EXE)
15+
16+
int main(int argc, char **argv) {
17+
void *region_without_hooks = HeapAlloc(GetProcessHeap(), 0, 10);
18+
HMODULE lib = LoadLibraryA(argv[1]);
19+
assert(lib != INVALID_HANDLE_VALUE);
20+
assert(0 != FreeLibrary(lib));
21+
assert(0 != HeapFree(GetProcessHeap(), 0, region_without_hooks));
22+
HeapReAlloc(GetProcessHeap(), 0, region_without_hooks, 100); //should throw nested error
23+
}
24+
#elif defined(DLL)
25+
// This global is registered at startup.
26+
27+
BOOL WINAPI DllMain(HMODULE, DWORD reason, LPVOID) {
28+
fprintf(stderr, "in DLL(reason=%d)\n", (int)reason);
29+
fflush(0);
30+
return TRUE;
31+
}
32+
33+
// CHECK: in DLL(reason=1)
34+
// CHECK: in DLL(reason=0)
35+
// CHECK: AddressSanitizer: nested bug in the same thread, aborting.
36+
37+
#else
38+
#error oops!
39+
#endif
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %clang_cl_asan -O0 %s -Fe%t
2+
// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s
3+
// XFAIL: asan-64-bits
4+
#include <cassert>
5+
#include <windows.h>
6+
7+
int main() {
8+
void *allocation = HeapAlloc(GetProcessHeap(), 0, 10);
9+
assert(allocation != 0);
10+
assert(HeapFree(GetProcessHeap(), 0, allocation));
11+
HeapFree(GetProcessHeap(), 0, allocation); //will dump
12+
assert(0 && "HeapFree double free should produce an ASAN dump\n");
13+
return 0;
14+
}
15+
16+
// CHECK: AddressSanitizer: attempting double-free on [[addr:0x[0-9a-fA-F]+]] in thread T0:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %clang_cl_asan -O0 %s -Fe%t
2+
// RUN: %run %t 2>&1 | FileCheck %s
3+
// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s
4+
// XFAIL: asan-64-bits
5+
#include <assert.h>
6+
#include <stdio.h>
7+
#include <windows.h>
8+
9+
extern "C" int
10+
__sanitizer_get_ownership(const volatile void *p);
11+
12+
int main() {
13+
char *buffer;
14+
buffer = (char *)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, 32);
15+
buffer[0] = 'a';
16+
assert(!__sanitizer_get_ownership(buffer));
17+
HeapFree(GetProcessHeap(), 0, buffer);
18+
puts("Okay");
19+
// CHECK: Okay
20+
}

0 commit comments

Comments
 (0)