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