Return memory to OS right after free (not in the async thread).
[platform/upstream/linaro-gcc.git] / libsanitizer / sanitizer_common / sanitizer_allocator.cc
1 //===-- sanitizer_allocator.cc --------------------------------------------===//
2 //
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
5 //
6 //===----------------------------------------------------------------------===//
7 //
8 // This file is shared between AddressSanitizer and ThreadSanitizer
9 // run-time libraries.
10 // This allocator is used inside run-times.
11 //===----------------------------------------------------------------------===//
12
13 #include "sanitizer_allocator.h"
14
15 #include "sanitizer_allocator_internal.h"
16 #include "sanitizer_atomic.h"
17 #include "sanitizer_common.h"
18
19 namespace __sanitizer {
20
21 // ThreadSanitizer for Go uses libc malloc/free.
22 #if SANITIZER_GO || defined(SANITIZER_USE_MALLOC)
23 # if SANITIZER_LINUX && !SANITIZER_ANDROID
24 extern "C" void *__libc_malloc(uptr size);
25 #  if !SANITIZER_GO
26 extern "C" void *__libc_memalign(uptr alignment, uptr size);
27 #  endif
28 extern "C" void *__libc_realloc(void *ptr, uptr size);
29 extern "C" void __libc_free(void *ptr);
30 # else
31 #  include <stdlib.h>
32 #  define __libc_malloc malloc
33 #  if !SANITIZER_GO
34 static void *__libc_memalign(uptr alignment, uptr size) {
35   void *p;
36   uptr error = posix_memalign(&p, alignment, size);
37   if (error) return nullptr;
38   return p;
39 }
40 #  endif
41 #  define __libc_realloc realloc
42 #  define __libc_free free
43 # endif
44
45 static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache,
46                               uptr alignment) {
47   (void)cache;
48 #if !SANITIZER_GO
49   if (alignment == 0)
50     return __libc_malloc(size);
51   else
52     return __libc_memalign(alignment, size);
53 #else
54   // Windows does not provide __libc_memalign/posix_memalign. It provides
55   // __aligned_malloc, but the allocated blocks can't be passed to free,
56   // they need to be passed to __aligned_free. InternalAlloc interface does
57   // not account for such requirement. Alignemnt does not seem to be used
58   // anywhere in runtime, so just call __libc_malloc for now.
59   DCHECK_EQ(alignment, 0);
60   return __libc_malloc(size);
61 #endif
62 }
63
64 static void *RawInternalRealloc(void *ptr, uptr size,
65                                 InternalAllocatorCache *cache) {
66   (void)cache;
67   return __libc_realloc(ptr, size);
68 }
69
70 static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {
71   (void)cache;
72   __libc_free(ptr);
73 }
74
75 InternalAllocator *internal_allocator() {
76   return 0;
77 }
78
79 #else  // SANITIZER_GO || defined(SANITIZER_USE_MALLOC)
80
81 static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)];
82 static atomic_uint8_t internal_allocator_initialized;
83 static StaticSpinMutex internal_alloc_init_mu;
84
85 static InternalAllocatorCache internal_allocator_cache;
86 static StaticSpinMutex internal_allocator_cache_mu;
87
88 InternalAllocator *internal_allocator() {
89   InternalAllocator *internal_allocator_instance =
90       reinterpret_cast<InternalAllocator *>(&internal_alloc_placeholder);
91   if (atomic_load(&internal_allocator_initialized, memory_order_acquire) == 0) {
92     SpinMutexLock l(&internal_alloc_init_mu);
93     if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) ==
94         0) {
95       internal_allocator_instance->Init(
96           /* may_return_null */ false, kReleaseToOSIntervalNever);
97       atomic_store(&internal_allocator_initialized, 1, memory_order_release);
98     }
99   }
100   return internal_allocator_instance;
101 }
102
103 static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache,
104                               uptr alignment) {
105   if (alignment == 0) alignment = 8;
106   if (cache == 0) {
107     SpinMutexLock l(&internal_allocator_cache_mu);
108     return internal_allocator()->Allocate(&internal_allocator_cache, size,
109                                           alignment, false);
110   }
111   return internal_allocator()->Allocate(cache, size, alignment, false);
112 }
113
114 static void *RawInternalRealloc(void *ptr, uptr size,
115                                 InternalAllocatorCache *cache) {
116   uptr alignment = 8;
117   if (cache == 0) {
118     SpinMutexLock l(&internal_allocator_cache_mu);
119     return internal_allocator()->Reallocate(&internal_allocator_cache, ptr,
120                                             size, alignment);
121   }
122   return internal_allocator()->Reallocate(cache, ptr, size, alignment);
123 }
124
125 static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {
126   if (!cache) {
127     SpinMutexLock l(&internal_allocator_cache_mu);
128     return internal_allocator()->Deallocate(&internal_allocator_cache, ptr);
129   }
130   internal_allocator()->Deallocate(cache, ptr);
131 }
132
133 #endif  // SANITIZER_GO || defined(SANITIZER_USE_MALLOC)
134
135 const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull;
136
137 void *InternalAlloc(uptr size, InternalAllocatorCache *cache, uptr alignment) {
138   if (size + sizeof(u64) < size)
139     return nullptr;
140   void *p = RawInternalAlloc(size + sizeof(u64), cache, alignment);
141   if (!p)
142     return nullptr;
143   ((u64*)p)[0] = kBlockMagic;
144   return (char*)p + sizeof(u64);
145 }
146
147 void *InternalRealloc(void *addr, uptr size, InternalAllocatorCache *cache) {
148   if (!addr)
149     return InternalAlloc(size, cache);
150   if (size + sizeof(u64) < size)
151     return nullptr;
152   addr = (char*)addr - sizeof(u64);
153   size = size + sizeof(u64);
154   CHECK_EQ(kBlockMagic, ((u64*)addr)[0]);
155   void *p = RawInternalRealloc(addr, size, cache);
156   if (!p)
157     return nullptr;
158   return (char*)p + sizeof(u64);
159 }
160
161 void *InternalCalloc(uptr count, uptr size, InternalAllocatorCache *cache) {
162   if (CallocShouldReturnNullDueToOverflow(count, size))
163     return internal_allocator()->ReturnNullOrDieOnBadRequest();
164   void *p = InternalAlloc(count * size, cache);
165   if (p) internal_memset(p, 0, count * size);
166   return p;
167 }
168
169 void InternalFree(void *addr, InternalAllocatorCache *cache) {
170   if (!addr)
171     return;
172   addr = (char*)addr - sizeof(u64);
173   CHECK_EQ(kBlockMagic, ((u64*)addr)[0]);
174   ((u64*)addr)[0] = 0;
175   RawInternalFree(addr, cache);
176 }
177
178 // LowLevelAllocator
179 static LowLevelAllocateCallback low_level_alloc_callback;
180
181 void *LowLevelAllocator::Allocate(uptr size) {
182   // Align allocation size.
183   size = RoundUpTo(size, 8);
184   if (allocated_end_ - allocated_current_ < (sptr)size) {
185     uptr size_to_allocate = Max(size, GetPageSizeCached());
186     allocated_current_ =
187         (char*)MmapOrDie(size_to_allocate, __func__);
188     allocated_end_ = allocated_current_ + size_to_allocate;
189     if (low_level_alloc_callback) {
190       low_level_alloc_callback((uptr)allocated_current_,
191                                size_to_allocate);
192     }
193   }
194   CHECK(allocated_end_ - allocated_current_ >= (sptr)size);
195   void *res = allocated_current_;
196   allocated_current_ += size;
197   return res;
198 }
199
200 void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) {
201   low_level_alloc_callback = callback;
202 }
203
204 bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) {
205   if (!size) return false;
206   uptr max = (uptr)-1L;
207   return (max / size) < n;
208 }
209
210 static atomic_uint8_t reporting_out_of_memory = {0};
211
212 bool IsReportingOOM() { return atomic_load_relaxed(&reporting_out_of_memory); }
213
214 void NORETURN ReportAllocatorCannotReturnNull(bool out_of_memory) {
215   if (out_of_memory) atomic_store_relaxed(&reporting_out_of_memory, 1);
216   Report("%s's allocator is terminating the process instead of returning 0\n",
217          SanitizerToolName);
218   Report("If you don't like this behavior set allocator_may_return_null=1\n");
219   CHECK(0);
220   Die();
221 }
222
223 } // namespace __sanitizer