| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 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/process/memory.h" | 5 #include "base/process/memory.h" |
| 6 | 6 |
| 7 #include <CoreFoundation/CoreFoundation.h> | 7 #include <CoreFoundation/CoreFoundation.h> |
| 8 #include <errno.h> | 8 #include <errno.h> |
| 9 #include <mach/mach.h> | 9 #include <mach/mach.h> |
| 10 #include <mach/mach_vm.h> | 10 #include <mach/mach_vm.h> |
| 11 #include <malloc/malloc.h> | 11 #include <malloc/malloc.h> |
| 12 #import <objc/runtime.h> | 12 #import <objc/runtime.h> |
| 13 | 13 |
| 14 #include <new> | 14 #include <new> |
| 15 | 15 |
| 16 #include "base/lazy_instance.h" | 16 #include "base/lazy_instance.h" |
| 17 #include "base/logging.h" | 17 #include "base/logging.h" |
| 18 #include "base/mac/mac_util.h" | 18 #include "base/mac/mac_util.h" |
| 19 #include "base/mac/mach_logging.h" | |
| 20 #include "base/scoped_clear_errno.h" | 19 #include "base/scoped_clear_errno.h" |
| 21 #include "third_party/apple_apsl/CFBase.h" | 20 #include "third_party/apple_apsl/CFBase.h" |
| 22 #include "third_party/apple_apsl/malloc.h" | 21 #include "third_party/apple_apsl/malloc.h" |
| 23 | 22 |
| 24 #if ARCH_CPU_32_BITS | 23 #if ARCH_CPU_32_BITS |
| 25 #include <dlfcn.h> | 24 #include <dlfcn.h> |
| 26 #include <mach-o/nlist.h> | 25 #include <mach-o/nlist.h> |
| 27 | 26 |
| 28 #include "base/threading/thread_local.h" | 27 #include "base/threading/thread_local.h" |
| 29 #include "third_party/mach_override/mach_override.h" | 28 #include "third_party/mach_override/mach_override.h" |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 216 struct vm_region_basic_info_64 info; | 215 struct vm_region_basic_info_64 info; |
| 217 mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; | 216 mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; |
| 218 kern_return_t result = | 217 kern_return_t result = |
| 219 mach_vm_region(mach_task_self(), | 218 mach_vm_region(mach_task_self(), |
| 220 reprotection_start, | 219 reprotection_start, |
| 221 reprotection_length, | 220 reprotection_length, |
| 222 VM_REGION_BASIC_INFO_64, | 221 VM_REGION_BASIC_INFO_64, |
| 223 reinterpret_cast<vm_region_info_t>(&info), | 222 reinterpret_cast<vm_region_info_t>(&info), |
| 224 &count, | 223 &count, |
| 225 &unused); | 224 &unused); |
| 226 MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_region"; | 225 CHECK(result == KERN_SUCCESS); |
| 227 | 226 |
| 228 // The kernel always returns a null object for VM_REGION_BASIC_INFO_64, but | 227 result = mach_port_deallocate(mach_task_self(), unused); |
| 229 // balance it with a deallocate in case this ever changes. See 10.9.2 | 228 CHECK(result == KERN_SUCCESS); |
| 230 // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region. | |
| 231 mach_port_deallocate(mach_task_self(), unused); | |
| 232 | 229 |
| 233 // Does the region fully enclose the zone pointers? Possibly unwarranted | 230 // Does the region fully enclose the zone pointers? Possibly unwarranted |
| 234 // simplification used: using the size of a full version 8 malloc zone rather | 231 // simplification used: using the size of a full version 8 malloc zone rather |
| 235 // than the actual smaller size if the passed-in zone is not version 8. | 232 // than the actual smaller size if the passed-in zone is not version 8. |
| 236 CHECK(*reprotection_start <= | 233 CHECK(*reprotection_start <= |
| 237 reinterpret_cast<mach_vm_address_t>(default_zone)); | 234 reinterpret_cast<mach_vm_address_t>(default_zone)); |
| 238 mach_vm_size_t zone_offset = reinterpret_cast<mach_vm_size_t>(default_zone) - | 235 mach_vm_size_t zone_offset = reinterpret_cast<mach_vm_size_t>(default_zone) - |
| 239 reinterpret_cast<mach_vm_size_t>(*reprotection_start); | 236 reinterpret_cast<mach_vm_size_t>(*reprotection_start); |
| 240 CHECK(zone_offset + sizeof(ChromeMallocZone) <= *reprotection_length); | 237 CHECK(zone_offset + sizeof(ChromeMallocZone) <= *reprotection_length); |
| 241 | 238 |
| 242 if (info.protection & VM_PROT_WRITE) { | 239 if (info.protection & VM_PROT_WRITE) { |
| 243 // No change needed; the zone is already writable. | 240 // No change needed; the zone is already writable. |
| 244 *reprotection_start = 0; | 241 *reprotection_start = 0; |
| 245 *reprotection_length = 0; | 242 *reprotection_length = 0; |
| 246 *reprotection_value = VM_PROT_NONE; | 243 *reprotection_value = VM_PROT_NONE; |
| 247 } else { | 244 } else { |
| 248 *reprotection_value = info.protection; | 245 *reprotection_value = info.protection; |
| 249 result = mach_vm_protect(mach_task_self(), | 246 result = mach_vm_protect(mach_task_self(), |
| 250 *reprotection_start, | 247 *reprotection_start, |
| 251 *reprotection_length, | 248 *reprotection_length, |
| 252 false, | 249 false, |
| 253 info.protection | VM_PROT_WRITE); | 250 info.protection | VM_PROT_WRITE); |
| 254 MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; | 251 CHECK(result == KERN_SUCCESS); |
| 255 } | 252 } |
| 256 } | 253 } |
| 257 | 254 |
| 258 // === C malloc/calloc/valloc/realloc/posix_memalign === | 255 // === C malloc/calloc/valloc/realloc/posix_memalign === |
| 259 | 256 |
| 260 typedef void* (*malloc_type)(struct _malloc_zone_t* zone, | 257 typedef void* (*malloc_type)(struct _malloc_zone_t* zone, |
| 261 size_t size); | 258 size_t size); |
| 262 typedef void* (*calloc_type)(struct _malloc_zone_t* zone, | 259 typedef void* (*calloc_type)(struct _malloc_zone_t* zone, |
| 263 size_t num_items, | 260 size_t num_items, |
| 264 size_t size); | 261 size_t size); |
| (...skipping 377 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 642 } | 639 } |
| 643 | 640 |
| 644 // Restore protection if it was active. | 641 // Restore protection if it was active. |
| 645 | 642 |
| 646 if (default_reprotection_start) { | 643 if (default_reprotection_start) { |
| 647 kern_return_t result = mach_vm_protect(mach_task_self(), | 644 kern_return_t result = mach_vm_protect(mach_task_self(), |
| 648 default_reprotection_start, | 645 default_reprotection_start, |
| 649 default_reprotection_length, | 646 default_reprotection_length, |
| 650 false, | 647 false, |
| 651 default_reprotection_value); | 648 default_reprotection_value); |
| 652 MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; | 649 CHECK(result == KERN_SUCCESS); |
| 653 } | 650 } |
| 654 | 651 |
| 655 if (purgeable_reprotection_start) { | 652 if (purgeable_reprotection_start) { |
| 656 kern_return_t result = mach_vm_protect(mach_task_self(), | 653 kern_return_t result = mach_vm_protect(mach_task_self(), |
| 657 purgeable_reprotection_start, | 654 purgeable_reprotection_start, |
| 658 purgeable_reprotection_length, | 655 purgeable_reprotection_length, |
| 659 false, | 656 false, |
| 660 purgeable_reprotection_value); | 657 purgeable_reprotection_value); |
| 661 MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; | 658 CHECK(result == KERN_SUCCESS); |
| 662 } | 659 } |
| 663 #endif | 660 #endif |
| 664 | 661 |
| 665 // === C malloc_zone_batch_malloc === | 662 // === C malloc_zone_batch_malloc === |
| 666 | 663 |
| 667 // batch_malloc is omitted because the default malloc zone's implementation | 664 // batch_malloc is omitted because the default malloc zone's implementation |
| 668 // only supports batch_malloc for "tiny" allocations from the free list. It | 665 // only supports batch_malloc for "tiny" allocations from the free list. It |
| 669 // will fail for allocations larger than "tiny", and will only allocate as | 666 // will fail for allocations larger than "tiny", and will only allocate as |
| 670 // many blocks as it's able to from the free list. These factors mean that it | 667 // many blocks as it's able to from the free list. These factors mean that it |
| 671 // can return less than the requested memory even in a non-out-of-memory | 668 // can return less than the requested memory even in a non-out-of-memory |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 740 @selector(allocWithZone:)); | 737 @selector(allocWithZone:)); |
| 741 g_old_allocWithZone = reinterpret_cast<allocWithZone_t>( | 738 g_old_allocWithZone = reinterpret_cast<allocWithZone_t>( |
| 742 method_getImplementation(orig_method)); | 739 method_getImplementation(orig_method)); |
| 743 CHECK(g_old_allocWithZone) | 740 CHECK(g_old_allocWithZone) |
| 744 << "Failed to get allocWithZone allocation function."; | 741 << "Failed to get allocWithZone allocation function."; |
| 745 method_setImplementation(orig_method, | 742 method_setImplementation(orig_method, |
| 746 reinterpret_cast<IMP>(oom_killer_allocWithZone)); | 743 reinterpret_cast<IMP>(oom_killer_allocWithZone)); |
| 747 } | 744 } |
| 748 | 745 |
| 749 } // namespace base | 746 } // namespace base |
| OLD | NEW |