| 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 |