Skip to content

Commit 90a7c6c

Browse files
committed
[win/asan] Improve SharedReAlloc with HEAP_REALLOC_IN_PLACE_ONLY.
This patch allows reallocations in place if the size is below or equal to the initial allocated size.
1 parent db7475a commit 90a7c6c

File tree

4 files changed

+113
-5
lines changed

4 files changed

+113
-5
lines changed

compiler-rt/lib/asan/asan_allocator.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,17 @@ struct Allocator {
525525
return true;
526526
}
527527

528+
bool UpdateDeallocationStack(uptr addr, BufferedStackTrace *stack) {
529+
AsanChunk *m = GetAsanChunkByAddr(addr);
530+
if (!m) return false;
531+
if (atomic_load(&m->chunk_state, memory_order_acquire) != CHUNK_ALLOCATED)
532+
return false;
533+
if (m->Beg() != addr) return false;
534+
AsanThread *t = GetCurrentThread();
535+
m->SetFreeContext(t ? t->tid() : kMainTid, StackDepotPut(*stack));
536+
return true;
537+
}
538+
528539
// -------------------- Allocation/Deallocation routines ---------------
529540
void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack,
530541
AllocType alloc_type, bool can_fill) {
@@ -941,8 +952,8 @@ uptr AsanChunkView::AllocTid() const {
941952
}
942953

943954
uptr AsanChunkView::FreeTid() const {
944-
if (!IsQuarantined())
945-
return kInvalidTid;
955+
//if (!IsQuarantined())
956+
// return kInvalidTid;
946957
u32 tid = 0;
947958
u32 stack = 0;
948959
chunk_->GetFreeContext(tid, stack);
@@ -961,8 +972,8 @@ u32 AsanChunkView::GetAllocStackId() const {
961972
}
962973

963974
u32 AsanChunkView::GetFreeStackId() const {
964-
if (!IsQuarantined())
965-
return 0;
975+
//if (!IsQuarantined())
976+
// return 0;
966977
u32 tid = 0;
967978
u32 stack = 0;
968979
chunk_->GetFreeContext(tid, stack);
@@ -1119,6 +1130,10 @@ void asan_mz_force_unlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
11191130
instance.ForceUnlock();
11201131
}
11211132

1133+
int asan_update_deallocation_context(void* addr, BufferedStackTrace *stack) {
1134+
return instance.UpdateDeallocationStack((uptr)addr, stack);
1135+
}
1136+
11221137
} // namespace __asan
11231138

11241139
// --- Implementation of LSan-specific functions --- {{{1

compiler-rt/lib/asan/asan_allocator.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,8 @@ uptr asan_mz_size(const void *ptr);
292292
void asan_mz_force_lock();
293293
void asan_mz_force_unlock();
294294

295+
int asan_update_deallocation_context(void* addr, BufferedStackTrace *stack);
296+
295297
void PrintInternalAllocatorStats();
296298
void AsanSoftRssLimitExceededCallback(bool exceeded);
297299

compiler-rt/lib/asan/asan_malloc_win.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "asan_allocator.h"
1818
#include "asan_interceptors.h"
1919
#include "asan_internal.h"
20+
#include "asan_poisoning.h"
2021
#include "asan_stack.h"
2122
#include "interception/interception.h"
2223
#include <stddef.h>
@@ -296,7 +297,7 @@ void *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc,
296297
if (ownershipState == RTL ||
297298
(ownershipState == NEITHER && !only_asan_supported_flags)) {
298299
if (only_asan_supported_flags) {
299-
// if this is a conversion to ASAN upported flags, transfer this
300+
// if this is a conversion to ASAN supported flags, transfer this
300301
// allocation to the ASAN allocator
301302
void *replacement_alloc;
302303
if (dwFlags & HEAP_ZERO_MEMORY)
@@ -323,6 +324,27 @@ void *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc,
323324
}
324325

325326
if (ownershipState == ASAN && !only_asan_supported_flags) {
327+
328+
if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) {
329+
size_t old_usable_size = asan_malloc_usable_size(lpMem, pc, bp);
330+
if (dwBytes == old_usable_size) {
331+
// nothing to change
332+
return lpMem;
333+
} else if (dwBytes < 1 || dwBytes >= old_usable_size) {
334+
// growing with HEAP_REALLOC_IN_PLACE_ONLY is not supported.
335+
return nullptr;
336+
} else {
337+
// poison just the "freed" part
338+
uptr aligned_ptr = RoundUpTo((uptr)((char *)lpMem + dwBytes), ASAN_SHADOW_GRANULARITY);
339+
uptr aligned_size = RoundUpTo(dwBytes, ASAN_SHADOW_GRANULARITY);
340+
PoisonShadow(aligned_ptr, aligned_size, kAsanHeapFreeMagic);
341+
// unpoison the lower part, it could be poisoned by a previous realloc in place
342+
__asan_unpoison_memory_region((char *)lpMem, dwBytes);
343+
__asan::asan_update_deallocation_context(lpMem, &stack);
344+
return lpMem;
345+
}
346+
}
347+
326348
// Conversion to unsupported flags allocation,
327349
// transfer this allocation back to the original allocator.
328350
void *replacement_alloc = allocFunc(hHeap, dwFlags, dwBytes);
@@ -348,6 +370,7 @@ void *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc,
348370
}
349371
// asan_realloc will never reallocate in place, so for now this flag is
350372
// unsupported until we figure out a way to fake this.
373+
// Small exception when shrinking or staying below the inital size, see above.
351374
if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY)
352375
return nullptr;
353376

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// RUN: %clang_cl_asan %Od %s %Fe%t %MD
2+
// RUN: %env_asan_opts=windows_hook_rtl_allocators=true:halt_on_error=false not %run %t 2>&1 | FileCheck %s
3+
4+
#include <stdio.h>
5+
#include <windows.h>
6+
7+
using AllocateFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, SIZE_T);
8+
using ReAllocateFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, PVOID, SIZE_T);
9+
using FreeFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, PVOID);
10+
11+
int main() {
12+
HMODULE NtDllHandle = GetModuleHandle("ntdll.dll");
13+
if (!NtDllHandle) {
14+
puts("Couldn't load ntdll??");
15+
return -1;
16+
}
17+
18+
auto RtlAllocateHeap_ptr =
19+
(AllocateFunctionPtr)GetProcAddress(NtDllHandle, "RtlAllocateHeap");
20+
if (RtlAllocateHeap_ptr == 0) {
21+
puts("Couldn't RtlAllocateHeap");
22+
return -1;
23+
}
24+
25+
auto RtlReAllocateHeap_ptr =
26+
(ReAllocateFunctionPtr)GetProcAddress(NtDllHandle, "RtlReAllocateHeap");
27+
if (RtlReAllocateHeap_ptr == 0) {
28+
puts("Couldn't find RtlReAllocateHeap");
29+
return -1;
30+
}
31+
32+
auto RtlFreeHeap_ptr =
33+
(FreeFunctionPtr)GetProcAddress(NtDllHandle, "RtlFreeHeap");
34+
if (RtlFreeHeap_ptr == 0) {
35+
puts("Couldn't RtlFreeHeap");
36+
return -1;
37+
}
38+
39+
char *buffer;
40+
void *ret;
41+
buffer = (char *)RtlAllocateHeap_ptr(GetProcessHeap(), 0, 23),
42+
43+
ret = RtlReAllocateHeap_ptr(GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY,
44+
buffer, 7);
45+
if (!ret) {
46+
puts("returned nullptr");
47+
}
48+
buffer[6] = 'a';
49+
puts("Okay 6");
50+
fflush(stdout);
51+
// CHECK: Okay 6
52+
53+
ret = RtlReAllocateHeap_ptr(GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY,
54+
buffer, 15);
55+
if (!ret) {
56+
puts("returned nullptr");
57+
}
58+
buffer[14] = 'a';
59+
puts("Okay 14");
60+
fflush(stdout);
61+
// CHECK: Okay 14
62+
63+
buffer[15] = 'a';
64+
// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]]
65+
// CHECK: WRITE of size 1 at [[ADDR]] thread T0
66+
67+
RtlFreeHeap_ptr(GetProcessHeap(), 0, buffer);
68+
}

0 commit comments

Comments
 (0)