summaryrefslogtreecommitdiff
path: root/libasan/asan_malloc_win.cc
blob: 7389a2572487e50570f1aebedd1bbc370c38ed84 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//===-- asan_malloc_win.cc ------------------------------------------------===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// Windows-specific malloc interception.
//===----------------------------------------------------------------------===//
#ifdef _WIN32

#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_stack.h"
#include "interception/interception.h"

#include <stddef.h>

// ---------------------- Replacement functions ---------------- {{{1
using namespace __asan;  // NOLINT

// FIXME: Simply defining functions with the same signature in *.obj
// files overrides the standard functions in *.lib
// This works well for simple helloworld-like tests but might need to be
// revisited in the future.

extern "C" {
void free(void *ptr) {
  GET_STACK_TRACE_HERE_FOR_FREE(ptr);
  return asan_free(ptr, &stack);
}

void _free_dbg(void* ptr, int) {
  free(ptr);
}

void cfree(void *ptr) {
  CHECK(!"cfree() should not be used on Windows?");
}

void *malloc(size_t size) {
  GET_STACK_TRACE_HERE_FOR_MALLOC;
  return asan_malloc(size, &stack);
}

void* _malloc_dbg(size_t size, int , const char*, int) {
  return malloc(size);
}

void *calloc(size_t nmemb, size_t size) {
  GET_STACK_TRACE_HERE_FOR_MALLOC;
  return asan_calloc(nmemb, size, &stack);
}

void* _calloc_dbg(size_t n, size_t size, int, const char*, int) {
  return calloc(n, size);
}

void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
  return calloc(nmemb, size);
}

void *realloc(void *ptr, size_t size) {
  GET_STACK_TRACE_HERE_FOR_MALLOC;
  return asan_realloc(ptr, size, &stack);
}

void *_realloc_dbg(void *ptr, size_t size, int) {
  CHECK(!"_realloc_dbg should not exist!");
  return 0;
}

void* _recalloc(void* p, size_t n, size_t elem_size) {
  if (!p)
    return calloc(n, 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 _msize(void *ptr) {
  GET_STACK_TRACE_HERE_FOR_MALLOC;
  return asan_malloc_usable_size(ptr, &stack);
}

int _CrtDbgReport(int, const char*, int,
                  const char*, const char*, ...) {
  ShowStatsAndAbort();
}

int _CrtDbgReportW(int reportType, const wchar_t*, int,
                   const wchar_t*, const wchar_t*, ...) {
  ShowStatsAndAbort();
}

int _CrtSetReportMode(int, int) {
  return 0;
}
}  // extern "C"

using __interception::GetRealFunctionAddress;

// We don't want to include "windows.h" in this file to avoid extra attributes
// set on malloc/free etc (e.g. dllimport), so declare a few things manually:
extern "C" int __stdcall VirtualProtect(void* addr, size_t size,
                                        DWORD prot, DWORD *old_prot);
const int PAGE_EXECUTE_READWRITE = 0x40;

namespace __asan {
void ReplaceSystemMalloc() {
#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.
    DWORD old_prot, unused;
    CHECK(VirtualProtect(crt_malloc, 16, PAGE_EXECUTE_READWRITE, &old_prot));
    REAL(memset)(crt_malloc, 0xCC /* int 3 */, 16);  // just in case.

    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));

    CHECK(VirtualProtect(crt_malloc, 16, old_prot, &unused));

    // FYI: FlushInstructionCache is needed on Itanium etc but not on x86/x64.
  }

  // FIXME: investigate whether anything else is needed.
#endif
}
}  // namespace __asan

#endif  // _WIN32