| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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_util.h" | 5 #include "base/process_util.h" |
| 6 | 6 |
| 7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
| 8 #include <crt_externs.h> | 8 #include <crt_externs.h> |
| 9 #include <dlfcn.h> | 9 #include <dlfcn.h> |
| 10 #include <errno.h> | 10 #include <errno.h> |
| 11 #include <mach/mach.h> | 11 #include <mach/mach.h> |
| 12 #include <mach/mach_init.h> | 12 #include <mach/mach_init.h> |
| 13 #include <mach/mach_vm.h> | 13 #include <mach/mach_vm.h> |
| 14 #include <mach/shared_region.h> | 14 #include <mach/shared_region.h> |
| 15 #include <mach/task.h> | 15 #include <mach/task.h> |
| 16 #include <mach-o/dyld.h> | |
| 17 #include <mach-o/nlist.h> | 16 #include <mach-o/nlist.h> |
| 18 #include <malloc/malloc.h> | 17 #include <malloc/malloc.h> |
| 19 #import <objc/runtime.h> | 18 #import <objc/runtime.h> |
| 20 #include <signal.h> | 19 #include <signal.h> |
| 21 #include <spawn.h> | 20 #include <spawn.h> |
| 22 #include <sys/event.h> | 21 #include <sys/event.h> |
| 23 #include <sys/mman.h> | 22 #include <sys/mman.h> |
| 24 #include <sys/sysctl.h> | 23 #include <sys/sysctl.h> |
| 25 #include <sys/types.h> | 24 #include <sys/types.h> |
| 26 #include <sys/wait.h> | 25 #include <sys/wait.h> |
| 27 | 26 |
| 28 #include <new> | 27 #include <new> |
| 29 #include <string> | 28 #include <string> |
| 30 | 29 |
| 31 #include "base/debug/debugger.h" | 30 #include "base/debug/debugger.h" |
| 32 #include "base/eintr_wrapper.h" | 31 #include "base/eintr_wrapper.h" |
| 33 #include "base/file_util.h" | 32 #include "base/file_util.h" |
| 34 #include "base/hash_tables.h" | 33 #include "base/hash_tables.h" |
| 34 #include "base/lazy_instance.h" |
| 35 #include "base/logging.h" | 35 #include "base/logging.h" |
| 36 #include "base/mac/mac_util.h" | 36 #include "base/mac/mac_util.h" |
| 37 #include "base/string_util.h" | 37 #include "base/string_util.h" |
| 38 #include "base/sys_info.h" | 38 #include "base/sys_info.h" |
| 39 #include "base/sys_string_conversions.h" | 39 #include "base/threading/thread_local.h" |
| 40 #include "base/time.h" | |
| 41 #include "third_party/apple_apsl/CFBase.h" | 40 #include "third_party/apple_apsl/CFBase.h" |
| 42 #include "third_party/apple_apsl/malloc.h" | 41 #include "third_party/apple_apsl/malloc.h" |
| 43 #include "third_party/mach_override/mach_override.h" | 42 #include "third_party/mach_override/mach_override.h" |
| 44 | 43 |
| 45 namespace base { | 44 namespace base { |
| 46 | 45 |
| 47 void RestoreDefaultExceptionHandler() { | 46 void RestoreDefaultExceptionHandler() { |
| 48 // This function is tailored to remove the Breakpad exception handler. | 47 // This function is tailored to remove the Breakpad exception handler. |
| 49 // exception_mask matches s_exception_mask in | 48 // exception_mask matches s_exception_mask in |
| 50 // breakpad/src/client/mac/handler/exception_handler.cc | 49 // breakpad/src/client/mac/handler/exception_handler.cc |
| (...skipping 508 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 559 if (errno == 0) | 558 if (errno == 0) |
| 560 errno = old_errno_; | 559 errno = old_errno_; |
| 561 } | 560 } |
| 562 | 561 |
| 563 private: | 562 private: |
| 564 int old_errno_; | 563 int old_errno_; |
| 565 | 564 |
| 566 DISALLOW_COPY_AND_ASSIGN(ScopedClearErrno); | 565 DISALLOW_COPY_AND_ASSIGN(ScopedClearErrno); |
| 567 }; | 566 }; |
| 568 | 567 |
| 568 // Combines ThreadLocalBoolean with AutoReset. It would be convenient |
| 569 // to compose ThreadLocalPointer<bool> with AutoReset<bool>, but that |
| 570 // would require allocating some storage for the bool. |
| 571 class ThreadLocalBooleanAutoReset { |
| 572 public: |
| 573 ThreadLocalBooleanAutoReset(ThreadLocalBoolean* tlb, bool new_value) |
| 574 : scoped_tlb_(tlb), |
| 575 original_value_(tlb->Get()) { |
| 576 scoped_tlb_->Set(new_value); |
| 577 } |
| 578 ~ThreadLocalBooleanAutoReset() { |
| 579 scoped_tlb_->Set(original_value_); |
| 580 } |
| 581 |
| 582 private: |
| 583 ThreadLocalBoolean* scoped_tlb_; |
| 584 bool original_value_; |
| 585 |
| 586 DISALLOW_COPY_AND_ASSIGN(ThreadLocalBooleanAutoReset); |
| 587 }; |
| 588 |
| 589 base::LazyInstance<ThreadLocalBoolean>::Leaky |
| 590 g_unchecked_malloc = LAZY_INSTANCE_INITIALIZER; |
| 591 |
| 569 void CrMallocErrorBreak() { | 592 void CrMallocErrorBreak() { |
| 570 g_original_malloc_error_break(); | 593 g_original_malloc_error_break(); |
| 571 | 594 |
| 572 // Out of memory is certainly not heap corruption, and not necessarily | 595 // Out of memory is certainly not heap corruption, and not necessarily |
| 573 // something for which the process should be terminated. Leave that decision | 596 // something for which the process should be terminated. Leave that decision |
| 574 // to the OOM killer. | 597 // to the OOM killer. The EBADF case comes up because the malloc library |
| 575 if (errno == ENOMEM) | 598 // attempts to log to ASL (syslog) before calling this code, which fails |
| 599 // accessing a Unix-domain socket because of sandboxing. |
| 600 if (errno == ENOMEM || (errno == EBADF && g_unchecked_malloc.Get().Get())) |
| 576 return; | 601 return; |
| 577 | 602 |
| 578 // A unit test checks this error message, so it needs to be in release builds. | 603 // A unit test checks this error message, so it needs to be in release builds. |
| 579 LOG(ERROR) << | 604 PLOG(ERROR) << |
| 580 "Terminating process due to a potential for future heap corruption"; | 605 "Terminating process due to a potential for future heap corruption"; |
| 581 int* volatile death_ptr = NULL; | 606 |
| 582 *death_ptr = 0xf00bad; | 607 // Crash by writing to NULL+errno to allow analyzing errno from |
| 608 // crash dump info (setting a breakpad key would re-enter the malloc |
| 609 // library). Max documented errno in intro(2) is actually 102, but |
| 610 // it really just needs to be "small" to stay on the right vm page. |
| 611 const int kMaxErrno = 256; |
| 612 char* volatile death_ptr = NULL; |
| 613 death_ptr += std::min(errno, kMaxErrno); |
| 614 *death_ptr = '!'; |
| 583 } | 615 } |
| 584 | 616 |
| 585 } // namespace | 617 } // namespace |
| 586 | 618 |
| 587 void EnableTerminationOnHeapCorruption() { | 619 void EnableTerminationOnHeapCorruption() { |
| 588 #ifdef ADDRESS_SANITIZER | 620 #ifdef ADDRESS_SANITIZER |
| 589 // Don't do anything special on heap corruption, because it should be handled | 621 // Don't do anything special on heap corruption, because it should be handled |
| 590 // by AddressSanitizer. | 622 // by AddressSanitizer. |
| 591 return; | 623 return; |
| 592 #endif | 624 #endif |
| (...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 829 id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone) | 861 id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone) |
| 830 { | 862 { |
| 831 id result = g_old_allocWithZone(self, _cmd, zone); | 863 id result = g_old_allocWithZone(self, _cmd, zone); |
| 832 if (!result) | 864 if (!result) |
| 833 debug::BreakDebugger(); | 865 debug::BreakDebugger(); |
| 834 return result; | 866 return result; |
| 835 } | 867 } |
| 836 | 868 |
| 837 } // namespace | 869 } // namespace |
| 838 | 870 |
| 839 malloc_zone_t* GetPurgeableZone() { | 871 void* UncheckedMalloc(size_t size) { |
| 840 // malloc_default_purgeable_zone only exists on >= 10.6. Use dlsym to grab it | 872 if (g_old_malloc) { |
| 841 // at runtime because it may not be present in the SDK used for compilation. | 873 ScopedClearErrno clear_errno; |
| 842 typedef malloc_zone_t* (*malloc_default_purgeable_zone_t)(void); | 874 ThreadLocalBooleanAutoReset flag(g_unchecked_malloc.Pointer(), true); |
| 843 malloc_default_purgeable_zone_t malloc_purgeable_zone = | 875 return g_old_malloc(malloc_default_zone(), size); |
| 844 reinterpret_cast<malloc_default_purgeable_zone_t>( | 876 } |
| 845 dlsym(RTLD_DEFAULT, "malloc_default_purgeable_zone")); | 877 return malloc(size); |
| 846 if (malloc_purgeable_zone) | |
| 847 return malloc_purgeable_zone(); | |
| 848 return NULL; | |
| 849 } | 878 } |
| 850 | 879 |
| 851 void EnableTerminationOnOutOfMemory() { | 880 void EnableTerminationOnOutOfMemory() { |
| 852 if (g_oom_killer_enabled) | 881 if (g_oom_killer_enabled) |
| 853 return; | 882 return; |
| 854 | 883 |
| 855 g_oom_killer_enabled = true; | 884 g_oom_killer_enabled = true; |
| 856 | 885 |
| 857 // === C malloc/calloc/valloc/realloc/posix_memalign === | 886 // === C malloc/calloc/valloc/realloc/posix_memalign === |
| 858 | 887 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 873 #if !defined(ADDRESS_SANITIZER) | 902 #if !defined(ADDRESS_SANITIZER) |
| 874 // Don't do anything special on OOM for the malloc zones replaced by | 903 // Don't do anything special on OOM for the malloc zones replaced by |
| 875 // AddressSanitizer, as modifying or protecting them may not work correctly. | 904 // AddressSanitizer, as modifying or protecting them may not work correctly. |
| 876 | 905 |
| 877 // See http://trac.webkit.org/changeset/53362/trunk/Tools/DumpRenderTree/mac | 906 // See http://trac.webkit.org/changeset/53362/trunk/Tools/DumpRenderTree/mac |
| 878 bool zone_allocators_protected = base::mac::IsOSLionOrLater(); | 907 bool zone_allocators_protected = base::mac::IsOSLionOrLater(); |
| 879 | 908 |
| 880 ChromeMallocZone* default_zone = | 909 ChromeMallocZone* default_zone = |
| 881 reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); | 910 reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); |
| 882 ChromeMallocZone* purgeable_zone = | 911 ChromeMallocZone* purgeable_zone = |
| 883 reinterpret_cast<ChromeMallocZone*>(GetPurgeableZone()); | 912 reinterpret_cast<ChromeMallocZone*>(malloc_default_purgeable_zone()); |
| 884 | 913 |
| 885 vm_address_t page_start_default = 0; | 914 vm_address_t page_start_default = 0; |
| 886 vm_address_t page_start_purgeable = 0; | 915 vm_address_t page_start_purgeable = 0; |
| 887 vm_size_t len_default = 0; | 916 vm_size_t len_default = 0; |
| 888 vm_size_t len_purgeable = 0; | 917 vm_size_t len_purgeable = 0; |
| 889 if (zone_allocators_protected) { | 918 if (zone_allocators_protected) { |
| 890 page_start_default = reinterpret_cast<vm_address_t>(default_zone) & | 919 page_start_default = reinterpret_cast<vm_address_t>(default_zone) & |
| 891 static_cast<vm_size_t>(~(getpagesize() - 1)); | 920 static_cast<vm_size_t>(~(getpagesize() - 1)); |
| 892 len_default = reinterpret_cast<vm_address_t>(default_zone) - | 921 len_default = reinterpret_cast<vm_address_t>(default_zone) - |
| 893 page_start_default + sizeof(ChromeMallocZone); | 922 page_start_default + sizeof(ChromeMallocZone); |
| (...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1209 } | 1238 } |
| 1210 } | 1239 } |
| 1211 | 1240 |
| 1212 } // namespace | 1241 } // namespace |
| 1213 | 1242 |
| 1214 void EnsureProcessTerminated(ProcessHandle process) { | 1243 void EnsureProcessTerminated(ProcessHandle process) { |
| 1215 WaitForChildToDie(process, kWaitBeforeKillSeconds); | 1244 WaitForChildToDie(process, kWaitBeforeKillSeconds); |
| 1216 } | 1245 } |
| 1217 | 1246 |
| 1218 } // namespace base | 1247 } // namespace base |
| OLD | NEW |