Commit 22d691ec authored by Gabriel Marin's avatar Gabriel Marin Committed by Commit Bot

tcmalloc: add a guard page in front of metadata allocations

Based on original CL:
- https://codereview.chromium.org/8570023

  Add a guard page in front of metadata allocations.

  BUG=104752
  Committed:
  http://src.chromium.org/viewvc/chrome?view=rev&revision=112260

Code has been modified to create the guard page inside MetaDataAlloc,
once per 8MB chunk.

BUG=724399,b:70905156

Change-Id: I527fbfe5e258cc052e205d01bfe2dd30e21b9f13
Reviewed-on: https://chromium-review.googlesource.com/c/1130794Reviewed-by: default avatarWill Harris <wfh@chromium.org>
Commit-Queue: Gabriel Marin <gmx@chromium.org>
Cr-Commit-Position: refs/heads/master@{#595983}
parent 4e1d4919
...@@ -38,6 +38,10 @@ ...@@ -38,6 +38,10 @@
#include "base/spinlock.h" #include "base/spinlock.h"
#include "getenv_safe.h" // TCMallocGetenvSafe #include "getenv_safe.h" // TCMallocGetenvSafe
#if defined(HAVE_UNISTD_H) && defined(HAVE_GETPAGESIZE)
#include <unistd.h> // for getpagesize
#endif
namespace tcmalloc { namespace tcmalloc {
// Define the maximum number of object per classe type to transfer between // Define the maximum number of object per classe type to transfer between
...@@ -235,7 +239,8 @@ void SizeMap::Init() { ...@@ -235,7 +239,8 @@ void SizeMap::Init() {
// Metadata allocator -- keeps stats about how many bytes allocated. // Metadata allocator -- keeps stats about how many bytes allocated.
static uint64_t metadata_system_bytes_ = 0; static uint64_t metadata_system_bytes_ = 0;
static const size_t kMetadataAllocChunkSize = 8*1024*1024;
static const size_t kMetadataAllocChunkSize = 8 * 1024 * 1024;
// As ThreadCache objects are allocated with MetaDataAlloc, and also // As ThreadCache objects are allocated with MetaDataAlloc, and also
// CACHELINE_ALIGNED, we must use the same alignment as TCMalloc_SystemAlloc. // CACHELINE_ALIGNED, we must use the same alignment as TCMalloc_SystemAlloc.
static const size_t kMetadataAllignment = sizeof(MemoryAligner); static const size_t kMetadataAllignment = sizeof(MemoryAligner);
...@@ -246,11 +251,23 @@ static size_t metadata_chunk_avail_; ...@@ -246,11 +251,23 @@ static size_t metadata_chunk_avail_;
static SpinLock metadata_alloc_lock(SpinLock::LINKER_INITIALIZED); static SpinLock metadata_alloc_lock(SpinLock::LINKER_INITIALIZED);
void* MetaDataAlloc(size_t bytes) { void* MetaDataAlloc(size_t bytes) {
if (bytes >= kMetadataAllocChunkSize) { static size_t pagesize;
void *rv = TCMalloc_SystemAlloc(bytes, #ifdef HAVE_GETPAGESIZE
NULL, kMetadataAllignment); if (pagesize == 0)
pagesize = getpagesize();
#endif
if (bytes + pagesize >= kMetadataAllocChunkSize) {
void* rv = TCMalloc_SystemAlloc(bytes + pagesize, NULL, pagesize);
if (rv != NULL) { if (rv != NULL) {
metadata_system_bytes_ += bytes; metadata_system_bytes_ += bytes + pagesize;
// This guard page protects the metadata from being corrupted by a
// buffer overrun. We currently have no mechanism for freeing it, since
// we never release the metadata buffer. If that changes we'll need to
// add something like TCMalloc_SystemRemoveGuard.
TCMalloc_SystemAddGuard(rv, bytes + pagesize);
rv = static_cast<void*>(static_cast<char*>(rv) + pagesize);
} }
return rv; return rv;
} }
...@@ -266,14 +283,16 @@ void* MetaDataAlloc(size_t bytes) { ...@@ -266,14 +283,16 @@ void* MetaDataAlloc(size_t bytes) {
if (metadata_chunk_avail_ < bytes + alignment) { if (metadata_chunk_avail_ < bytes + alignment) {
size_t real_size; size_t real_size;
void *ptr = TCMalloc_SystemAlloc(kMetadataAllocChunkSize, void* ptr =
&real_size, kMetadataAllignment); TCMalloc_SystemAlloc(kMetadataAllocChunkSize, &real_size, pagesize);
if (ptr == NULL) { if (ptr == NULL) {
return NULL; return NULL;
} }
metadata_chunk_alloc_ = static_cast<char *>(ptr); TCMalloc_SystemAddGuard(ptr, kMetadataAllocChunkSize);
metadata_chunk_avail_ = real_size; metadata_chunk_alloc_ = static_cast<char*>(ptr) + pagesize;
metadata_chunk_avail_ = real_size - pagesize;
metadata_system_bytes_ += pagesize;
alignment = 0; alignment = 0;
} }
......
...@@ -305,9 +305,9 @@ class SizeMap { ...@@ -305,9 +305,9 @@ class SizeMap {
} }
}; };
// Allocates "bytes" worth of memory and returns it. Increments // Allocates "bytes" worth of memory with a guard page in front and
// metadata_system_bytes appropriately. May return NULL if allocation // returns it. Increments metadata_system_bytes appropriately. May
// fails. Requires pageheap_lock is held. // return NULL if allocation fails. Requires pageheap_lock is held.
void* MetaDataAlloc(size_t bytes); void* MetaDataAlloc(size_t bytes);
// Returns the total number of bytes allocated from the system. // Returns the total number of bytes allocated from the system.
......
...@@ -94,6 +94,7 @@ static const bool kDebugMode = true; ...@@ -94,6 +94,7 @@ static const bool kDebugMode = true;
#endif #endif
// TODO(sanjay): Move the code below into the tcmalloc namespace // TODO(sanjay): Move the code below into the tcmalloc namespace
using tcmalloc::kCrash;
using tcmalloc::kLog; using tcmalloc::kLog;
using tcmalloc::Log; using tcmalloc::Log;
...@@ -250,9 +251,10 @@ COMPILE_ASSERT(kAddressBits <= 8 * sizeof(void*), ...@@ -250,9 +251,10 @@ COMPILE_ASSERT(kAddressBits <= 8 * sizeof(void*),
static SpinLock spinlock(SpinLock::LINKER_INITIALIZED); static SpinLock spinlock(SpinLock::LINKER_INITIALIZED);
#if defined(HAVE_MMAP) || defined(MADV_FREE) #if defined(HAVE_MMAP) || defined(MADV_FREE)
// Page size is initialized on demand (only needed for mmap-based allocators) #ifdef HAVE_GETPAGESIZE
static size_t pagesize = 0; static size_t pagesize = 0;
#endif #endif
#endif
// The current system allocator // The current system allocator
SysAllocator* tcmalloc_sys_alloc = NULL; SysAllocator* tcmalloc_sys_alloc = NULL;
...@@ -660,6 +662,27 @@ void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size, ...@@ -660,6 +662,27 @@ void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size,
return result; return result;
} }
void TCMalloc_SystemAddGuard(void* start, size_t size) {
#ifdef HAVE_GETPAGESIZE
if (pagesize == 0)
pagesize = getpagesize();
if (size < pagesize || (reinterpret_cast<size_t>(start) % pagesize) != 0) {
Log(kCrash, __FILE__, __LINE__,
"FATAL ERROR: alloc size (%d) < pagesize (%d), or start address (%p) "
"is not page aligned\n",
size, pagesize, start);
return;
}
if (mprotect(start, pagesize, PROT_NONE)) {
Log(kCrash, __FILE__, __LINE__,
"FATAL ERROR: mprotect(%p, %d, PROT_NONE) failed: %s\n", start,
pagesize, strerror(errno));
}
#endif
}
bool TCMalloc_SystemRelease(void* start, size_t length) { bool TCMalloc_SystemRelease(void* start, size_t length) {
#ifdef MADV_FREE #ifdef MADV_FREE
if (FLAGS_malloc_devmem_start) { if (FLAGS_malloc_devmem_start) {
......
...@@ -83,6 +83,10 @@ bool TCMalloc_SystemRelease(void* start, size_t length); ...@@ -83,6 +83,10 @@ bool TCMalloc_SystemRelease(void* start, size_t length);
extern PERFTOOLS_DLL_DECL extern PERFTOOLS_DLL_DECL
void TCMalloc_SystemCommit(void* start, size_t length); void TCMalloc_SystemCommit(void* start, size_t length);
// Guards the first page in the supplied range of memory. It crashes the
// program if the guard page cannot be added.
extern void TCMalloc_SystemAddGuard(void* start, size_t size);
// The current system allocator. // The current system allocator.
extern PERFTOOLS_DLL_DECL SysAllocator* tcmalloc_sys_alloc; extern PERFTOOLS_DLL_DECL SysAllocator* tcmalloc_sys_alloc;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment