OLD | NEW |
---|---|
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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 // This file contains all the logic necessary to intercept allocations on | 5 // This file contains all the logic necessary to intercept allocations on |
6 // macOS. "malloc zones" are an abstraction that allows the process to intercept | 6 // macOS. "malloc zones" are an abstraction that allows the process to intercept |
7 // all malloc-related functions. There is no good mechanism [short of | 7 // all malloc-related functions. There is no good mechanism [short of |
8 // interposition] to determine new malloc zones are added, so there's no clean | 8 // interposition] to determine new malloc zones are added, so there's no clean |
9 // mechanism to intercept all malloc zones. This file contains logic to | 9 // mechanism to intercept all malloc zones. This file contains logic to |
10 // intercept the default and purgeable zones, which always exist. A cursory | 10 // intercept the default and purgeable zones, which always exist. A cursory |
(...skipping 20 matching lines...) Expand all Loading... | |
31 #include "base/mac/mac_util.h" | 31 #include "base/mac/mac_util.h" |
32 #include "base/mac/mach_logging.h" | 32 #include "base/mac/mach_logging.h" |
33 #include "base/process/memory.h" | 33 #include "base/process/memory.h" |
34 #include "base/scoped_clear_errno.h" | 34 #include "base/scoped_clear_errno.h" |
35 #include "build/build_config.h" | 35 #include "build/build_config.h" |
36 #include "third_party/apple_apsl/CFBase.h" | 36 #include "third_party/apple_apsl/CFBase.h" |
37 | 37 |
38 namespace base { | 38 namespace base { |
39 namespace allocator { | 39 namespace allocator { |
40 | 40 |
41 MallocZoneFunctions::MallocZoneFunctions() {} | |
42 | |
41 namespace { | 43 namespace { |
42 | 44 |
43 bool g_oom_killer_enabled; | 45 bool g_oom_killer_enabled; |
46 bool g_replaced_default_zone = false; | |
44 | 47 |
45 // Starting with Mac OS X 10.7, the zone allocators set up by the system are | 48 // Starting with Mac OS X 10.7, the zone allocators set up by the system are |
46 // read-only, to prevent them from being overwritten in an attack. However, | 49 // read-only, to prevent them from being overwritten in an attack. However, |
47 // blindly unprotecting and reprotecting the zone allocators fails with | 50 // blindly unprotecting and reprotecting the zone allocators fails with |
48 // GuardMalloc because GuardMalloc sets up its zone allocator using a block of | 51 // GuardMalloc because GuardMalloc sets up its zone allocator using a block of |
49 // memory in its bss. Explicit saving/restoring of the protection is required. | 52 // memory in its bss. Explicit saving/restoring of the protection is required. |
50 // | 53 // |
51 // This function takes a pointer to a malloc zone, de-protects it if necessary, | 54 // This function takes a pointer to a malloc zone, de-protects it if necessary, |
52 // and returns (in the out parameters) a region of memory (if any) to be | 55 // and returns (in the out parameters) a region of memory (if any) to be |
53 // re-protected when modifications are complete. This approach assumes that | 56 // re-protected when modifications are complete. This approach assumes that |
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
295 return *result != NULL; | 298 return *result != NULL; |
296 } | 299 } |
297 | 300 |
298 void StoreZoneFunctions(ChromeMallocZone* zone, | 301 void StoreZoneFunctions(ChromeMallocZone* zone, |
299 MallocZoneFunctions* functions) { | 302 MallocZoneFunctions* functions) { |
300 functions->malloc = zone->malloc; | 303 functions->malloc = zone->malloc; |
301 functions->calloc = zone->calloc; | 304 functions->calloc = zone->calloc; |
302 functions->valloc = zone->valloc; | 305 functions->valloc = zone->valloc; |
303 functions->free = zone->free; | 306 functions->free = zone->free; |
304 functions->realloc = zone->realloc; | 307 functions->realloc = zone->realloc; |
308 functions->size = zone->size; | |
305 CHECK(functions->malloc && functions->calloc && functions->valloc && | 309 CHECK(functions->malloc && functions->calloc && functions->valloc && |
306 functions->free && functions->realloc); | 310 functions->free && functions->realloc && functions->size); |
311 | |
312 // These functions might be nullptr. | |
313 functions->batch_malloc = zone->batch_malloc; | |
314 functions->batch_free = zone->batch_free; | |
307 | 315 |
308 if (zone->version >= 5) { | 316 if (zone->version >= 5) { |
309 functions->memalign = zone->memalign; | 317 functions->memalign = zone->memalign; |
310 CHECK(functions->memalign); | 318 CHECK(functions->memalign); |
311 } | 319 } |
320 if (zone->version >= 6) { | |
321 // This may be nullptr. | |
322 functions->free_definite_size = zone->free_definite_size; | |
323 } | |
312 } | 324 } |
313 | 325 |
314 void ReplaceZoneFunctions(ChromeMallocZone* zone, | 326 void ReplaceZoneFunctions(ChromeMallocZone* zone, |
315 const MallocZoneFunctions* functions) { | 327 const MallocZoneFunctions* functions) { |
316 // Remove protection. | 328 // Remove protection. |
317 mach_vm_address_t reprotection_start = 0; | 329 mach_vm_address_t reprotection_start = 0; |
318 mach_vm_size_t reprotection_length = 0; | 330 mach_vm_size_t reprotection_length = 0; |
319 vm_prot_t reprotection_value = VM_PROT_NONE; | 331 vm_prot_t reprotection_value = VM_PROT_NONE; |
320 DeprotectMallocZone(zone, &reprotection_start, &reprotection_length, | 332 DeprotectMallocZone(zone, &reprotection_start, &reprotection_length, |
321 &reprotection_value); | 333 &reprotection_value); |
322 | 334 |
335 CHECK(functions->malloc && functions->calloc && functions->valloc && | |
336 functions->free && functions->realloc); | |
323 zone->malloc = functions->malloc; | 337 zone->malloc = functions->malloc; |
324 zone->calloc = functions->calloc; | 338 zone->calloc = functions->calloc; |
325 zone->valloc = functions->valloc; | 339 zone->valloc = functions->valloc; |
326 zone->free = functions->free; | 340 zone->free = functions->free; |
327 zone->realloc = functions->realloc; | 341 zone->realloc = functions->realloc; |
328 if (zone->version >= 5) { | 342 if (functions->batch_malloc) |
343 zone->batch_malloc = functions->batch_malloc; | |
344 if (functions->batch_free) | |
345 zone->batch_free = functions->batch_free; | |
346 if (functions->size) | |
347 zone->size = functions->size; | |
348 if (zone->version >= 5 && functions->memalign) { | |
329 zone->memalign = functions->memalign; | 349 zone->memalign = functions->memalign; |
330 } | 350 } |
351 if (zone->version >= 6 && functions->free_definite_size) { | |
352 zone->free_definite_size = functions->free_definite_size; | |
353 } | |
331 | 354 |
332 // Restore protection if it was active. | 355 // Restore protection if it was active. |
333 if (reprotection_start) { | 356 if (reprotection_start) { |
334 kern_return_t result = | 357 kern_return_t result = |
335 mach_vm_protect(mach_task_self(), reprotection_start, | 358 mach_vm_protect(mach_task_self(), reprotection_start, |
336 reprotection_length, false, reprotection_value); | 359 reprotection_length, false, reprotection_value); |
337 MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; | 360 MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; |
338 } | 361 } |
339 } | 362 } |
340 | 363 |
364 void StoreDefaultZoneFunctions(MallocZoneFunctions* functions) { | |
Primiano Tucci (use gerrit)
2017/01/28 05:10:02
purely a naming thing, but this tripped my brain f
erikchen
2017/01/31 02:21:22
changed name to StoreFunctionsForDefaultZone
| |
365 ChromeMallocZone* default_zone = | |
366 reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); | |
367 StoreZoneFunctions(default_zone, functions); | |
368 } | |
369 | |
370 void ReplaceDefaultZoneFunctions(const MallocZoneFunctions* functions) { | |
371 CHECK(!g_replaced_default_zone); | |
372 g_replaced_default_zone = true; | |
373 StoreDefaultZoneFunctions(&g_old_zone); | |
374 ChromeMallocZone* default_zone = | |
375 reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); | |
376 ReplaceZoneFunctions(default_zone, functions); | |
377 } | |
378 | |
341 void InterceptAllocationsMac() { | 379 void InterceptAllocationsMac() { |
342 if (g_oom_killer_enabled) | 380 if (g_oom_killer_enabled) |
343 return; | 381 return; |
344 | 382 |
345 g_oom_killer_enabled = true; | 383 g_oom_killer_enabled = true; |
346 | 384 |
347 // === C malloc/calloc/valloc/realloc/posix_memalign === | 385 // === C malloc/calloc/valloc/realloc/posix_memalign === |
348 | 386 |
349 // This approach is not perfect, as requests for amounts of memory larger than | 387 // This approach is not perfect, as requests for amounts of memory larger than |
350 // MALLOC_ABSOLUTE_MAX_SIZE (currently SIZE_T_MAX - (2 * PAGE_SIZE)) will | 388 // MALLOC_ABSOLUTE_MAX_SIZE (currently SIZE_T_MAX - (2 * PAGE_SIZE)) will |
351 // still fail with a NULL rather than dying (see | 389 // still fail with a NULL rather than dying (see |
352 // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c for details). | 390 // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c for details). |
353 // Unfortunately, it's the best we can do. Also note that this does not affect | 391 // Unfortunately, it's the best we can do. Also note that this does not affect |
354 // allocations from non-default zones. | 392 // allocations from non-default zones. |
355 | 393 |
356 #if !defined(ADDRESS_SANITIZER) | 394 #if !defined(ADDRESS_SANITIZER) |
357 // Don't do anything special on OOM for the malloc zones replaced by | 395 // Don't do anything special on OOM for the malloc zones replaced by |
358 // AddressSanitizer, as modifying or protecting them may not work correctly. | 396 // AddressSanitizer, as modifying or protecting them may not work correctly. |
359 ChromeMallocZone* default_zone = | 397 if (!g_replaced_default_zone) { |
360 reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); | 398 StoreDefaultZoneFunctions(&g_old_zone); |
361 StoreZoneFunctions(default_zone, &g_old_zone); | 399 MallocZoneFunctions new_functions; |
362 MallocZoneFunctions new_functions; | 400 new_functions.malloc = oom_killer_malloc; |
363 new_functions.malloc = oom_killer_malloc; | 401 new_functions.calloc = oom_killer_calloc; |
364 new_functions.calloc = oom_killer_calloc; | 402 new_functions.valloc = oom_killer_valloc; |
365 new_functions.valloc = oom_killer_valloc; | 403 new_functions.free = oom_killer_free; |
366 new_functions.free = oom_killer_free; | 404 new_functions.realloc = oom_killer_realloc; |
367 new_functions.realloc = oom_killer_realloc; | 405 new_functions.memalign = oom_killer_memalign; |
368 new_functions.memalign = oom_killer_memalign; | 406 ReplaceDefaultZoneFunctions(&new_functions); |
369 ReplaceZoneFunctions(default_zone, &new_functions); | 407 } |
370 | 408 |
371 ChromeMallocZone* purgeable_zone = | 409 ChromeMallocZone* purgeable_zone = |
372 reinterpret_cast<ChromeMallocZone*>(malloc_default_purgeable_zone()); | 410 reinterpret_cast<ChromeMallocZone*>(malloc_default_purgeable_zone()); |
373 if (purgeable_zone) { | 411 if (purgeable_zone) { |
374 StoreZoneFunctions(purgeable_zone, &g_old_purgeable_zone); | 412 StoreZoneFunctions(purgeable_zone, &g_old_purgeable_zone); |
375 MallocZoneFunctions new_functions; | 413 MallocZoneFunctions new_functions; |
376 new_functions.malloc = oom_killer_malloc_purgeable; | 414 new_functions.malloc = oom_killer_malloc_purgeable; |
377 new_functions.calloc = oom_killer_calloc_purgeable; | 415 new_functions.calloc = oom_killer_calloc_purgeable; |
378 new_functions.valloc = oom_killer_valloc_purgeable; | 416 new_functions.valloc = oom_killer_valloc_purgeable; |
379 new_functions.free = oom_killer_free_purgeable; | 417 new_functions.free = oom_killer_free_purgeable; |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
462 g_old_allocWithZone = | 500 g_old_allocWithZone = |
463 reinterpret_cast<allocWithZone_t>(method_getImplementation(orig_method)); | 501 reinterpret_cast<allocWithZone_t>(method_getImplementation(orig_method)); |
464 CHECK(g_old_allocWithZone) | 502 CHECK(g_old_allocWithZone) |
465 << "Failed to get allocWithZone allocation function."; | 503 << "Failed to get allocWithZone allocation function."; |
466 method_setImplementation(orig_method, | 504 method_setImplementation(orig_method, |
467 reinterpret_cast<IMP>(oom_killer_allocWithZone)); | 505 reinterpret_cast<IMP>(oom_killer_allocWithZone)); |
468 } | 506 } |
469 | 507 |
470 } // namespace allocator | 508 } // namespace allocator |
471 } // namespace base | 509 } // namespace base |
OLD | NEW |