[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 llvm-svn: 359498
This commit is contained in:
parent
6da0989cc4
commit
d62416dfcd
|
@ -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
|
||||
|
@ -132,18 +154,6 @@ void *_recalloc_base(void *p, size_t n, size_t elem_size) {
|
|||
return _recalloc(p, n, 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.
|
||||
|
@ -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,
|
||||
|
|
|
@ -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!
|
|
@ -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
|
Loading…
Reference in New Issue