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 19 matching lines...) Expand all Loading... |
30 #include "base/allocator/allocator_shim.h" | 30 #include "base/allocator/allocator_shim.h" |
31 #include "base/allocator/features.h" | 31 #include "base/allocator/features.h" |
32 #include "base/allocator/malloc_zone_functions_mac.h" | 32 #include "base/allocator/malloc_zone_functions_mac.h" |
33 #include "base/logging.h" | 33 #include "base/logging.h" |
34 #include "base/mac/mac_util.h" | 34 #include "base/mac/mac_util.h" |
35 #include "base/mac/mach_logging.h" | 35 #include "base/mac/mach_logging.h" |
36 #include "base/process/memory.h" | 36 #include "base/process/memory.h" |
37 #include "base/scoped_clear_errno.h" | 37 #include "base/scoped_clear_errno.h" |
38 #include "build/build_config.h" | 38 #include "build/build_config.h" |
39 #include "third_party/apple_apsl/CFBase.h" | 39 #include "third_party/apple_apsl/CFBase.h" |
| 40 #include "third_party/mach_override/mach_override.h" |
40 | 41 |
41 namespace base { | 42 namespace base { |
42 namespace allocator { | 43 namespace allocator { |
43 | 44 |
44 bool g_replaced_default_zone = false; | 45 bool g_replaced_default_zone = false; |
45 | 46 |
46 namespace { | 47 namespace { |
47 | 48 |
48 bool g_oom_killer_enabled; | 49 bool g_oom_killer_enabled; |
49 | 50 |
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
295 | 296 |
296 // Restore protection if it was active. | 297 // Restore protection if it was active. |
297 if (reprotection_start) { | 298 if (reprotection_start) { |
298 kern_return_t result = | 299 kern_return_t result = |
299 mach_vm_protect(mach_task_self(), reprotection_start, | 300 mach_vm_protect(mach_task_self(), reprotection_start, |
300 reprotection_length, false, reprotection_value); | 301 reprotection_length, false, reprotection_value); |
301 MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; | 302 MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; |
302 } | 303 } |
303 } | 304 } |
304 | 305 |
| 306 typedef void (*MallocZoneRegisterType)(malloc_zone_t* zone); |
| 307 MallocZoneRegisterType g_original_malloc_zone_register = nullptr; |
| 308 MallocZoneFunctions g_malloc_zone_functions; |
| 309 bool g_update_malloc_zone_during_interception = true; |
| 310 |
| 311 void ChromeMallocZoneRegister(malloc_zone_t* new_zone) { |
| 312 DCHECK(g_original_malloc_zone_register); |
| 313 if (!g_update_malloc_zone_during_interception) { |
| 314 g_original_malloc_zone_register(new_zone); |
| 315 return; |
| 316 } |
| 317 |
| 318 ChromeMallocZone* zone = reinterpret_cast<ChromeMallocZone*>(new_zone); |
| 319 |
| 320 if (!StoreMallocZone(zone)) |
| 321 return; |
| 322 |
| 323 ReplaceZoneFunctions(zone, &g_malloc_zone_functions); |
| 324 g_original_malloc_zone_register(new_zone); |
| 325 } |
| 326 |
| 327 typedef struct _malloc_zone_t* (*MallocCreateZoneType)(vm_size_t start_size, |
| 328 unsigned flags); |
| 329 MallocCreateZoneType g_original_malloc_create_zone = nullptr; |
| 330 |
| 331 struct _malloc_zone_t* ChromeMallocCreateZone(vm_size_t start_size, |
| 332 unsigned flags) { |
| 333 DCHECK(g_original_malloc_create_zone); |
| 334 |
| 335 struct _malloc_zone_t* new_zone = |
| 336 g_original_malloc_create_zone(start_size, flags); |
| 337 if (!g_update_malloc_zone_during_interception) { |
| 338 return new_zone; |
| 339 } |
| 340 |
| 341 ChromeMallocZone* zone = reinterpret_cast<ChromeMallocZone*>(new_zone); |
| 342 |
| 343 if (!StoreMallocZone(zone)) |
| 344 return new_zone; |
| 345 |
| 346 ReplaceZoneFunctions(zone, &g_malloc_zone_functions); |
| 347 return new_zone; |
| 348 } |
| 349 |
305 void UninterceptMallocZoneForTesting(struct _malloc_zone_t* zone) { | 350 void UninterceptMallocZoneForTesting(struct _malloc_zone_t* zone) { |
306 ChromeMallocZone* chrome_zone = reinterpret_cast<ChromeMallocZone*>(zone); | 351 ChromeMallocZone* chrome_zone = reinterpret_cast<ChromeMallocZone*>(zone); |
307 if (!IsMallocZoneAlreadyStored(chrome_zone)) | 352 if (!IsMallocZoneAlreadyStored(chrome_zone)) |
308 return; | 353 return; |
309 MallocZoneFunctions& functions = GetFunctionsForZone(zone); | 354 MallocZoneFunctions& functions = GetFunctionsForZone(zone); |
310 ReplaceZoneFunctions(chrome_zone, &functions); | 355 ReplaceZoneFunctions(chrome_zone, &functions); |
311 } | 356 } |
312 | 357 |
313 } // namespace | 358 } // namespace |
314 | 359 |
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
506 Method orig_method = | 551 Method orig_method = |
507 class_getClassMethod(nsobject_class, @selector(allocWithZone:)); | 552 class_getClassMethod(nsobject_class, @selector(allocWithZone:)); |
508 g_old_allocWithZone = | 553 g_old_allocWithZone = |
509 reinterpret_cast<allocWithZone_t>(method_getImplementation(orig_method)); | 554 reinterpret_cast<allocWithZone_t>(method_getImplementation(orig_method)); |
510 CHECK(g_old_allocWithZone) | 555 CHECK(g_old_allocWithZone) |
511 << "Failed to get allocWithZone allocation function."; | 556 << "Failed to get allocWithZone allocation function."; |
512 method_setImplementation(orig_method, | 557 method_setImplementation(orig_method, |
513 reinterpret_cast<IMP>(oom_killer_allocWithZone)); | 558 reinterpret_cast<IMP>(oom_killer_allocWithZone)); |
514 } | 559 } |
515 | 560 |
| 561 void InterceptNewlyRegisteredMallocZones(const MallocZoneFunctions* functions) { |
| 562 g_update_malloc_zone_during_interception = true; |
| 563 |
| 564 // In a typical Chrome build, InterceptNewlyRegisteredMallocZones should only |
| 565 // be called once. In a testing environment, it might be called more often. |
| 566 if (g_original_malloc_zone_register) |
| 567 return; |
| 568 |
| 569 memcpy(&g_malloc_zone_functions, functions, sizeof(MallocZoneFunctions)); |
| 570 mach_error_t err = mach_override_ptr( |
| 571 reinterpret_cast<void*>(malloc_zone_register), |
| 572 reinterpret_cast<void*>(ChromeMallocZoneRegister), |
| 573 reinterpret_cast<void**>(&g_original_malloc_zone_register)); |
| 574 if (err != err_none) { |
| 575 DLOG(WARNING) << "mach_override malloc_zone_register: " << err; |
| 576 } |
| 577 |
| 578 // On macOS 10.9, the implementation of malloc_create_zone calls |
| 579 // malloc_zone_register_while_locked, a private symbol in libmalloc, so we |
| 580 // also need to interpose it. Starting from macOS 10.10.0, libmalloc was open |
| 581 // sourced, and the implementation of malloc_create_zone calls |
| 582 // malloc_zone_register. |
| 583 if (base::mac::IsOS10_9()) { |
| 584 err = mach_override_ptr( |
| 585 reinterpret_cast<void*>(malloc_create_zone), |
| 586 reinterpret_cast<void*>(ChromeMallocCreateZone), |
| 587 reinterpret_cast<void**>(&g_original_malloc_create_zone)); |
| 588 if (err != err_none) { |
| 589 DLOG(WARNING) << "mach_override malloc_create_zone: " << err; |
| 590 } |
| 591 } |
| 592 } |
| 593 |
| 594 void StopMallocZoneRegistrationInterceptionForTesting() { |
| 595 g_update_malloc_zone_during_interception = false; |
| 596 } |
| 597 |
516 void UninterceptMallocZonesForTesting() { | 598 void UninterceptMallocZonesForTesting() { |
517 UninterceptMallocZoneForTesting(malloc_default_zone()); | 599 UninterceptMallocZoneForTesting(malloc_default_zone()); |
518 vm_address_t* zones; | 600 vm_address_t* zones; |
519 unsigned int count; | 601 unsigned int count; |
520 kern_return_t kr = malloc_get_all_zones(mach_task_self(), 0, &zones, &count); | 602 kern_return_t kr = malloc_get_all_zones(mach_task_self(), 0, &zones, &count); |
521 CHECK(kr == KERN_SUCCESS); | 603 CHECK(kr == KERN_SUCCESS); |
522 for (unsigned int i = 0; i < count; ++i) { | 604 for (unsigned int i = 0; i < count; ++i) { |
523 UninterceptMallocZoneForTesting( | 605 UninterceptMallocZoneForTesting( |
524 reinterpret_cast<struct _malloc_zone_t*>(zones[i])); | 606 reinterpret_cast<struct _malloc_zone_t*>(zones[i])); |
525 } | 607 } |
526 | 608 |
527 ClearAllMallocZonesForTesting(); | 609 ClearAllMallocZonesForTesting(); |
| 610 StopMallocZoneRegistrationInterceptionForTesting(); |
528 } | 611 } |
529 | 612 |
530 } // namespace allocator | 613 } // namespace allocator |
531 } // namespace base | 614 } // namespace base |
OLD | NEW |