| 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 StoreFunctionsForDefaultZone(MallocZoneFunctions* functions) { |
| 365 ChromeMallocZone* default_zone = reinterpret_cast<ChromeMallocZone*>( |
| 366 malloc_default_zone()); |
| 367 StoreZoneFunctions(default_zone, functions); |
| 368 } |
| 369 |
| 370 void ReplaceFunctionsForDefaultZone(const MallocZoneFunctions* functions) { |
| 371 CHECK(!g_replaced_default_zone); |
| 372 g_replaced_default_zone = true; |
| 373 StoreFunctionsForDefaultZone(&g_old_zone); |
| 374 ChromeMallocZone* default_zone = reinterpret_cast<ChromeMallocZone*>( |
| 375 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 StoreFunctionsForDefaultZone(&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 ReplaceFunctionsForDefaultZone(&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 |