summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew G McGovern <matthew.mcgovern@microsoft.com>2019-04-29 20:26:19 +0000
committerMatthew G McGovern <matthew.mcgovern@microsoft.com>2019-04-29 20:26:19 +0000
commit89cc6f2230ae2e1052693aea3921945d92ce814f (patch)
treef8d2249ed06e28cf98b123f18efd72ff88267ec5
parentb33a9ba70c193d96cbe696d565d8b6e7874929b6 (diff)
downloadcompiler-rt-89cc6f2230ae2e1052693aea3921945d92ce814f.tar.gz
[AddressSanitizer] [Windows] Fix HeapReAlloc and _recalloc bugs in asan_malloc_win.cc
HeapReAlloc should allow for 0 sized reallocations without freeing the memory block provided by the user. _recalloc previously did not zero new memory after reallocation. https://reviews.llvm.org/D61268 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@359498 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/asan/asan_malloc_win.cc57
-rw-r--r--test/asan/TestCases/Windows/heaprealloc_zero_size.cc21
-rw-r--r--test/asan/TestCases/Windows/recalloc_sanity.cc37
3 files changed, 101 insertions, 14 deletions
diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc
index 752976567..c16ed9b34 100644
--- a/lib/asan/asan_malloc_win.cc
+++ b/lib/asan/asan_malloc_win.cc
@@ -47,6 +47,19 @@ using namespace __asan; // NOLINT
#endif
extern "C" {
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+size_t _msize(void *ptr) {
+ GET_CURRENT_PC_BP_SP;
+ (void)sp;
+ return asan_malloc_usable_size(ptr, pc, bp);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+size_t _msize_base(void *ptr) {
+ return _msize(ptr);
+}
+
ALLOCATION_FUNCTION_ATTRIBUTE
void free(void *ptr) {
GET_STACK_TRACE_FREE;
@@ -124,7 +137,16 @@ void *_recalloc(void *p, size_t n, size_t elem_size) {
const size_t size = n * elem_size;
if (elem_size != 0 && size / elem_size != n)
return 0;
- return realloc(p, size);
+
+ size_t old_size = _msize(p);
+ void *new_alloc = malloc(size);
+ if (new_alloc) {
+ REAL(memcpy)(new_alloc, p, Min(size, old_size));
+ if (old_size < size)
+ REAL(memset)(((u8 *)new_alloc) + old_size, 0, size - old_size);
+ free(p);
+ }
+ return new_alloc;
}
ALLOCATION_FUNCTION_ATTRIBUTE
@@ -133,18 +155,6 @@ void *_recalloc_base(void *p, size_t n, size_t elem_size) {
}
ALLOCATION_FUNCTION_ATTRIBUTE
-size_t _msize(void *ptr) {
- GET_CURRENT_PC_BP_SP;
- (void)sp;
- return asan_malloc_usable_size(ptr, pc, bp);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-size_t _msize_base(void *ptr) {
- return _msize(ptr);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
void *_expand(void *memblock, size_t size) {
// _expand is used in realloc-like functions to resize the buffer if possible.
// We don't want memory to stand still while resizing buffers, so return 0.
@@ -198,11 +208,30 @@ INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) {
INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags,
LPVOID lpMem, SIZE_T dwBytes) {
GET_STACK_TRACE_MALLOC;
+ GET_CURRENT_PC_BP_SP;
// Realloc should never reallocate in place.
if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY)
return nullptr;
CHECK(dwFlags == 0 && "unsupported heap flags");
- return asan_realloc(lpMem, dwBytes, &stack);
+ // HeapReAlloc and HeapAlloc both happily accept 0 sized allocations.
+ // passing a 0 size into asan_realloc will free the allocation.
+ // To avoid this and keep behavior consistent, fudge the size if 0.
+ // (asan_malloc already does this)
+ if (dwBytes == 0)
+ dwBytes = 1;
+ size_t old_size;
+ if (dwFlags & HEAP_ZERO_MEMORY)
+ old_size = asan_malloc_usable_size(lpMem, pc, bp);
+ void *ptr = asan_realloc(lpMem, dwBytes, &stack);
+ if (ptr == nullptr)
+ return nullptr;
+
+ if (dwFlags & HEAP_ZERO_MEMORY) {
+ size_t new_size = asan_malloc_usable_size(ptr, pc, bp);
+ if (old_size < new_size)
+ REAL(memset)(((u8 *)ptr) + old_size, 0, new_size - old_size);
+ }
+ return ptr;
}
INTERCEPTOR_WINAPI(SIZE_T, HeapSize, HANDLE hHeap, DWORD dwFlags,
diff --git a/test/asan/TestCases/Windows/heaprealloc_zero_size.cc b/test/asan/TestCases/Windows/heaprealloc_zero_size.cc
new file mode 100644
index 000000000..3d60202ce
--- /dev/null
+++ b/test/asan/TestCases/Windows/heaprealloc_zero_size.cc
@@ -0,0 +1,21 @@
+// RUN: %clang_cl_asan /Od -o %t %s
+// RUN: %run %t 2>&1 | FileCheck %s
+// RUN: %clang_cl /Od -o %t %s
+// RUN: %run %t 2>&1 | FileCheck %s
+#include <cassert>
+#include <stdio.h>
+#include<windows.h>
+
+int main() {
+ HANDLE heap = HeapCreate(0, 0, 0);
+ void *ptr = HeapAlloc(heap, 0, 4);
+ assert(ptr);
+ void *ptr2 = HeapReAlloc(heap, 0, ptr, 0);
+ assert(ptr2);
+ HeapFree(heap, 0, ptr2);
+ fprintf(stderr, "passed!\n");
+}
+
+// CHECK-NOT: double-free
+// CHECK-NOT: AddressSanitizer
+// CHECK: passed! \ No newline at end of file
diff --git a/test/asan/TestCases/Windows/recalloc_sanity.cc b/test/asan/TestCases/Windows/recalloc_sanity.cc
new file mode 100644
index 000000000..41df5d002
--- /dev/null
+++ b/test/asan/TestCases/Windows/recalloc_sanity.cc
@@ -0,0 +1,37 @@
+// RUN: %clang_cl_asan %s -o %t.exe
+// RUN: %run %t.exe 2>&1 | FileCheck %s
+// RUN: %clang_cl %s -o %t.exe
+// RUN: %run %t.exe 2>&1 | FileCheck %s
+
+#include <cassert>
+#include <stdio.h>
+#include <windows.h>
+
+int main() {
+ void *p = calloc(1, 100);
+ assert(p);
+ void *np = _recalloc(p, 2, 100);
+ assert(np);
+ for (int i = 0; i < 2 * 100; i++) {
+ assert(((BYTE *)np)[i] == 0);
+ }
+ void *nnp = _recalloc(np, 1, 100);
+ assert(nnp);
+ for (int i = 0; i < 100; i++) {
+ assert(((BYTE *)nnp)[i] == 0);
+ ((BYTE *)nnp)[i] = 0x0d;
+ }
+ void *nnnp = _recalloc(nnp, 2, 100);
+ assert(nnnp);
+ for (int i = 0; i < 100; i++) {
+ assert(((BYTE *)nnnp)[i] == 0x0d);
+ }
+ for (int i = 100; i < 200; i++) {
+ assert(((BYTE *)nnnp)[i] == 0);
+ }
+ fprintf(stderr, "passed\n");
+ return 0;
+}
+
+// CHECK-NOT: Assertion
+// CHECK: passed \ No newline at end of file