Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(58)

Side by Side Diff: base/allocator/allocator_shim.cc

Issue 2138173002: Hookup the generic heap intercept for Windows. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Snip an errant curly. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/allocator/allocator_shim.h" 5 #include "base/allocator/allocator_shim.h"
6 6
7 #include <errno.h> 7 #include <errno.h>
8 #include <unistd.h>
9 8
10 #include <new> 9 #include <new>
11 10
12 #include "base/atomicops.h" 11 #include "base/atomicops.h"
13 #include "base/logging.h" 12 #include "base/logging.h"
14 #include "base/macros.h" 13 #include "base/macros.h"
15 #include "base/threading/platform_thread.h" 14 #include "base/threading/platform_thread.h"
16 #include "build/build_config.h" 15 #include "build/build_config.h"
17 16
17 #if !defined(OS_WIN)
18 #include <unistd.h>
19 #else
20 #include "base/allocator/winheap_stubs_win.h"
21 #endif
22
18 // No calls to malloc / new in this file. They would would cause re-entrancy of 23 // No calls to malloc / new in this file. They would would cause re-entrancy of
19 // the shim, which is hard to deal with. Keep this code as simple as possible 24 // the shim, which is hard to deal with. Keep this code as simple as possible
20 // and don't use any external C++ object here, not even //base ones. Even if 25 // and don't use any external C++ object here, not even //base ones. Even if
21 // they are safe to use today, in future they might be refactored. 26 // they are safe to use today, in future they might be refactored.
22 27
23 namespace { 28 namespace {
24 29
25 using namespace base; 30 using namespace base;
26 31
27 subtle::AtomicWord g_chain_head = reinterpret_cast<subtle::AtomicWord>( 32 subtle::AtomicWord g_chain_head = reinterpret_cast<subtle::AtomicWord>(
28 &allocator::AllocatorDispatch::default_dispatch); 33 &allocator::AllocatorDispatch::default_dispatch);
29 34
30 bool g_call_new_handler_on_malloc_failure = false; 35 bool g_call_new_handler_on_malloc_failure = false;
31 subtle::Atomic32 g_new_handler_lock = 0; 36 subtle::Atomic32 g_new_handler_lock = 0;
32 37
33 // In theory this should be just base::ThreadChecker. But we can't afford 38 // In theory this should be just base::ThreadChecker. But we can't afford
34 // the luxury of a LazyInstance<ThreadChecker> here as it would cause a new(). 39 // the luxury of a LazyInstance<ThreadChecker> here as it would cause a new().
35 bool CalledOnValidThread() { 40 bool CalledOnValidThread() {
36 using subtle::Atomic32; 41 using subtle::Atomic32;
37 const Atomic32 kInvalidTID = static_cast<Atomic32>(kInvalidThreadId); 42 const Atomic32 kInvalidTID = static_cast<Atomic32>(kInvalidThreadId);
38 static Atomic32 g_tid = kInvalidTID; 43 static Atomic32 g_tid = kInvalidTID;
39 Atomic32 cur_tid = static_cast<Atomic32>(PlatformThread::CurrentId()); 44 Atomic32 cur_tid = static_cast<Atomic32>(PlatformThread::CurrentId());
40 Atomic32 prev_tid = 45 Atomic32 prev_tid =
41 subtle::NoBarrier_CompareAndSwap(&g_tid, kInvalidTID, cur_tid); 46 subtle::NoBarrier_CompareAndSwap(&g_tid, kInvalidTID, cur_tid);
42 return prev_tid == kInvalidTID || prev_tid == cur_tid; 47 return prev_tid == kInvalidTID || prev_tid == cur_tid;
43 } 48 }
44 49
45 inline size_t GetPageSize() { 50 inline size_t GetPageSize() {
51 #if defined(OS_WIN)
52 return 4096;
Will Harris 2016/07/15 15:49:54 base::GetPageSize() is the canonical way, but it c
Sigurður Ásgeirsson 2016/07/18 19:27:06 Done.
53 #else
46 static size_t pagesize = 0; 54 static size_t pagesize = 0;
47 if (!pagesize) 55 if (!pagesize)
48 pagesize = sysconf(_SC_PAGESIZE); 56 pagesize = sysconf(_SC_PAGESIZE);
49 return pagesize; 57 return pagesize;
58 #endif
50 } 59 }
51 60
52 // Calls the std::new handler thread-safely. Returns true if a new_handler was 61 // Calls the std::new handler thread-safely. Returns true if a new_handler was
53 // set and called, false if no new_handler was set. 62 // set and called, false if no new_handler was set.
54 bool CallNewHandler() { 63 bool CallNewHandler(size_t size) {
64 #if defined(OS_WIN)
65 return base::allocator::WinCallNewHandler(size);
66 #else
55 // TODO(primiano): C++11 has introduced ::get_new_handler() which is supposed 67 // TODO(primiano): C++11 has introduced ::get_new_handler() which is supposed
56 // to be thread safe and would avoid the spinlock boilerplate here. However 68 // to be thread safe and would avoid the spinlock boilerplate here. However
57 // it doesn't seem to be available yet in the Linux chroot headers yet. 69 // it doesn't seem to be available yet in the Linux chroot headers yet.
58 std::new_handler nh; 70 std::new_handler nh;
59 { 71 {
60 while (subtle::Acquire_CompareAndSwap(&g_new_handler_lock, 0, 1)) 72 while (subtle::Acquire_CompareAndSwap(&g_new_handler_lock, 0, 1))
61 PlatformThread::YieldCurrentThread(); 73 PlatformThread::YieldCurrentThread();
62 nh = std::set_new_handler(0); 74 nh = std::set_new_handler(0);
63 ignore_result(std::set_new_handler(nh)); 75 ignore_result(std::set_new_handler(nh));
64 subtle::Release_Store(&g_new_handler_lock, 0); 76 subtle::Release_Store(&g_new_handler_lock, 0);
65 } 77 }
66 if (!nh) 78 if (!nh)
67 return false; 79 return false;
68 (*nh)(); 80 (*nh)();
69 // Assume the new_handler will abort if it fails. Exception are disabled and 81 // Assume the new_handler will abort if it fails. Exception are disabled and
70 // we don't support the case of a new_handler throwing std::bad_balloc. 82 // we don't support the case of a new_handler throwing std::bad_balloc.
71 return true; 83 return true;
84 #endif
72 } 85 }
73 86
74 inline const allocator::AllocatorDispatch* GetChainHead() { 87 inline const allocator::AllocatorDispatch* GetChainHead() {
75 // TODO(primiano): Just use NoBarrier_Load once crbug.com/593344 is fixed. 88 // TODO(primiano): Just use NoBarrier_Load once crbug.com/593344 is fixed.
76 // Unfortunately due to that bug NoBarrier_Load() is mistakenly fully 89 // Unfortunately due to that bug NoBarrier_Load() is mistakenly fully
77 // barriered on Linux+Clang, and that causes visible perf regressons. 90 // barriered on Linux+Clang, and that causes visible perf regressons.
78 return reinterpret_cast<const allocator::AllocatorDispatch*>( 91 return reinterpret_cast<const allocator::AllocatorDispatch*>(
79 #if defined(OS_LINUX) && defined(__clang__) 92 #if defined(OS_LINUX) && defined(__clang__)
80 *static_cast<const volatile subtle::AtomicWord*>(&g_chain_head) 93 *static_cast<const volatile subtle::AtomicWord*>(&g_chain_head)
81 #else 94 #else
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
141 // - If the std::new_handler is set: 154 // - If the std::new_handler is set:
142 // - Assume it will abort() if it fails (very likely the new_handler will 155 // - Assume it will abort() if it fails (very likely the new_handler will
143 // just suicide priting a message). 156 // just suicide priting a message).
144 // - Assume it did succeed if it returns, in which case reattempt the alloc. 157 // - Assume it did succeed if it returns, in which case reattempt the alloc.
145 158
146 void* ShimCppNew(size_t size) { 159 void* ShimCppNew(size_t size) {
147 const allocator::AllocatorDispatch* const chain_head = GetChainHead(); 160 const allocator::AllocatorDispatch* const chain_head = GetChainHead();
148 void* ptr; 161 void* ptr;
149 do { 162 do {
150 ptr = chain_head->alloc_function(chain_head, size); 163 ptr = chain_head->alloc_function(chain_head, size);
151 } while (!ptr && CallNewHandler()); 164 } while (!ptr && CallNewHandler(size));
152 return ptr; 165 return ptr;
153 } 166 }
154 167
155 void ShimCppDelete(void* address) { 168 void ShimCppDelete(void* address) {
156 const allocator::AllocatorDispatch* const chain_head = GetChainHead(); 169 const allocator::AllocatorDispatch* const chain_head = GetChainHead();
157 return chain_head->free_function(chain_head, address); 170 return chain_head->free_function(chain_head, address);
158 } 171 }
159 172
160 void* ShimMalloc(size_t size) { 173 void* ShimMalloc(size_t size) {
161 const allocator::AllocatorDispatch* const chain_head = GetChainHead(); 174 const allocator::AllocatorDispatch* const chain_head = GetChainHead();
162 void* ptr; 175 void* ptr;
163 do { 176 do {
164 ptr = chain_head->alloc_function(chain_head, size); 177 ptr = chain_head->alloc_function(chain_head, size);
165 } while (!ptr && g_call_new_handler_on_malloc_failure && CallNewHandler()); 178 } while (!ptr && g_call_new_handler_on_malloc_failure &&
179 CallNewHandler(size));
166 return ptr; 180 return ptr;
167 } 181 }
168 182
169 void* ShimCalloc(size_t n, size_t size) { 183 void* ShimCalloc(size_t n, size_t size) {
170 const allocator::AllocatorDispatch* const chain_head = GetChainHead(); 184 const allocator::AllocatorDispatch* const chain_head = GetChainHead();
171 void* ptr; 185 void* ptr;
172 do { 186 do {
173 ptr = chain_head->alloc_zero_initialized_function(chain_head, n, size); 187 ptr = chain_head->alloc_zero_initialized_function(chain_head, n, size);
174 } while (!ptr && g_call_new_handler_on_malloc_failure && CallNewHandler()); 188 } while (!ptr && g_call_new_handler_on_malloc_failure &&
189 CallNewHandler(size));
175 return ptr; 190 return ptr;
176 } 191 }
177 192
178 void* ShimRealloc(void* address, size_t size) { 193 void* ShimRealloc(void* address, size_t size) {
179 // realloc(size == 0) means free() and might return a nullptr. We should 194 // realloc(size == 0) means free() and might return a nullptr. We should
180 // not call the std::new_handler in that case, though. 195 // not call the std::new_handler in that case, though.
181 const allocator::AllocatorDispatch* const chain_head = GetChainHead(); 196 const allocator::AllocatorDispatch* const chain_head = GetChainHead();
182 void* ptr; 197 void* ptr;
183 do { 198 do {
184 ptr = chain_head->realloc_function(chain_head, address, size); 199 ptr = chain_head->realloc_function(chain_head, address, size);
185 } while (!ptr && size && g_call_new_handler_on_malloc_failure && 200 } while (!ptr && size && g_call_new_handler_on_malloc_failure &&
186 CallNewHandler()); 201 CallNewHandler(size));
187 return ptr; 202 return ptr;
188 } 203 }
189 204
190 void* ShimMemalign(size_t alignment, size_t size) { 205 void* ShimMemalign(size_t alignment, size_t size) {
191 const allocator::AllocatorDispatch* const chain_head = GetChainHead(); 206 const allocator::AllocatorDispatch* const chain_head = GetChainHead();
192 void* ptr; 207 void* ptr;
193 do { 208 do {
194 ptr = chain_head->alloc_aligned_function(chain_head, alignment, size); 209 ptr = chain_head->alloc_aligned_function(chain_head, alignment, size);
195 } while (!ptr && g_call_new_handler_on_malloc_failure && CallNewHandler()); 210 } while (!ptr && g_call_new_handler_on_malloc_failure &&
211 CallNewHandler(size));
196 return ptr; 212 return ptr;
197 } 213 }
198 214
199 int ShimPosixMemalign(void** res, size_t alignment, size_t size) { 215 int ShimPosixMemalign(void** res, size_t alignment, size_t size) {
200 // posix_memalign is supposed to check the arguments. See tc_posix_memalign() 216 // posix_memalign is supposed to check the arguments. See tc_posix_memalign()
201 // in tc_malloc.cc. 217 // in tc_malloc.cc.
202 if (((alignment % sizeof(void*)) != 0) || 218 if (((alignment % sizeof(void*)) != 0) ||
203 ((alignment & (alignment - 1)) != 0) || (alignment == 0)) { 219 ((alignment & (alignment - 1)) != 0) || (alignment == 0)) {
204 return EINVAL; 220 return EINVAL;
205 } 221 }
(...skipping 14 matching lines...) Expand all
220 size = (size + GetPageSize() - 1) & ~(GetPageSize() - 1); 236 size = (size + GetPageSize() - 1) & ~(GetPageSize() - 1);
221 } 237 }
222 return ShimMemalign(GetPageSize(), size); 238 return ShimMemalign(GetPageSize(), size);
223 } 239 }
224 240
225 void ShimFree(void* address) { 241 void ShimFree(void* address) {
226 const allocator::AllocatorDispatch* const chain_head = GetChainHead(); 242 const allocator::AllocatorDispatch* const chain_head = GetChainHead();
227 return chain_head->free_function(chain_head, address); 243 return chain_head->free_function(chain_head, address);
228 } 244 }
229 245
246 #if defined(OS_WIN)
247
248 void* (*malloc_unchecked)(size_t) = &base::allocator::UncheckedAlloc;
249
250 namespace {
251
252 int win_new_mode = 0;
253
254 } // namespace
255
256 // This function behaves similarly to MSVC's _set_new_mode.
257 // If flag is 0 (default), calls to malloc will behave normally.
258 // If flag is 1, calls to malloc will behave like calls to new,
259 // and the std_new_handler will be invoked on failure.
260 // Returns the previous mode.
261 //
262 // Replaces _set_new_mode in ucrt\heap\new_mode.cpp
263 int _set_new_mode(int flag) {
264 // The MS CRT calls this function early on in startup, so this serves as a low
265 // overhead proof that the allocator shim is in place for this process.
266 base::allocator::g_is_win_shim_layer_initialized = true;
267 int old_mode = win_new_mode;
268 win_new_mode = flag;
269
270 base::allocator::SetCallNewHandlerOnMallocFailure(win_new_mode != 0);
271
272 return old_mode;
273 }
274
275 // Replaces _query_new_mode in ucrt\heap\new_mode.cpp
276 int _query_new_mode() {
277 return win_new_mode;
278 }
279
280 #endif
281
230 } // extern "C" 282 } // extern "C"
231 283
232 // Cpp symbols (new / delete) should always be routed through the shim layer. 284 #if !defined(OS_WIN)
285 // Cpp symbols (new / delete) should always be routed through the shim layer
286 // except on Windows where the malloc intercept is deep enough that it also
287 // catches the cpp calls.
233 #include "base/allocator/allocator_shim_override_cpp_symbols.h" 288 #include "base/allocator/allocator_shim_override_cpp_symbols.h"
289 #endif
234 290
291 #if defined(OS_ANDROID)
235 // Android does not support symbol interposition. The way malloc symbols are 292 // Android does not support symbol interposition. The way malloc symbols are
236 // intercepted on Android is by using link-time -wrap flags. 293 // intercepted on Android is by using link-time -wrap flags.
237 #if !defined(OS_ANDROID) 294 #include "base/allocator/allocator_shim_override_linker_wrapped_symbols.h"
295 #elif defined(OS_WIN)
296 // On Windows we use plain link-time overriding of the CRT symbols.
297 #include "base/allocator/allocator_shim_override_ucrt_symbols_win.h"
298 #else
238 // Ditto for plain malloc() / calloc() / free() etc. symbols. 299 // Ditto for plain malloc() / calloc() / free() etc. symbols.
Primiano Tucci (use gerrit) 2016/07/15 14:02:10 plz remove this comment line. It was already uncom
Sigurður Ásgeirsson 2016/07/18 19:27:06 Done.
239 #include "base/allocator/allocator_shim_override_libc_symbols.h" 300 #include "base/allocator/allocator_shim_override_libc_symbols.h"
240 #else
241 #include "base/allocator/allocator_shim_override_linker_wrapped_symbols.h"
242 #endif 301 #endif
243 302
244 // In the case of tcmalloc we also want to plumb into the glibc hooks 303 // In the case of tcmalloc we also want to plumb into the glibc hooks
245 // to avoid that allocations made in glibc itself (e.g., strdup()) get 304 // to avoid that allocations made in glibc itself (e.g., strdup()) get
246 // accidentally performed on the glibc heap instead of the tcmalloc one. 305 // accidentally performed on the glibc heap instead of the tcmalloc one.
247 #if defined(USE_TCMALLOC) 306 #if defined(USE_TCMALLOC)
248 #include "base/allocator/allocator_shim_override_glibc_weak_symbols.h" 307 #include "base/allocator/allocator_shim_override_glibc_weak_symbols.h"
249 #endif 308 #endif
250 309
251 // Cross-checks. 310 // Cross-checks.
252 311
253 #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) 312 #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
254 #error The allocator shim should not be compiled when building for memory tools. 313 #error The allocator shim should not be compiled when building for memory tools.
255 #endif 314 #endif
256 315
257 #if (defined(__GNUC__) && defined(__EXCEPTIONS)) || \ 316 #if (defined(__GNUC__) && defined(__EXCEPTIONS)) || \
258 (defined(_HAS_EXCEPTIONS) && _HAS_EXCEPTIONS) 317 (defined(_HAS_EXCEPTIONS) && _HAS_EXCEPTIONS)
259 #error This code cannot be used when exceptions are turned on. 318 #error This code cannot be used when exceptions are turned on.
260 #endif 319 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698