[sanitizer] change the way SizeClassAllocator64 allocated memory from the system: instead of one huge mmap(NORESERVE) it does one huge mprotect and then does small on-demand mmaps. This allows us to call OnMap callbacks which are required to poison newly allocated memory in asan

llvm-svn: 170097
This commit is contained in:
Kostya Serebryany 2012-12-13 05:05:11 +00:00
parent 0f207edaa5
commit 2ce42e0aef
2 changed files with 26 additions and 4 deletions

View File

@ -132,6 +132,8 @@ struct NoOpMapUnmapCallback {
// Space: a portion of address space of kSpaceSize bytes starting at // Space: a portion of address space of kSpaceSize bytes starting at
// a fixed address (kSpaceBeg). Both constants are powers of two and // a fixed address (kSpaceBeg). Both constants are powers of two and
// kSpaceBeg is kSpaceSize-aligned. // kSpaceBeg is kSpaceSize-aligned.
// At the beginning the entire space is mprotect-ed, then small parts of it
// are mapped on demand.
// //
// Region: a part of Space dedicated to a single size class. // Region: a part of Space dedicated to a single size class.
// There are kNumClasses Regions of equal size. // There are kNumClasses Regions of equal size.
@ -147,8 +149,8 @@ template <const uptr kSpaceBeg, const uptr kSpaceSize,
class SizeClassAllocator64 { class SizeClassAllocator64 {
public: public:
void Init() { void Init() {
CHECK_EQ(kSpaceBeg, reinterpret_cast<uptr>(MmapFixedNoReserve( CHECK_EQ(kSpaceBeg,
kSpaceBeg, kSpaceSize))); reinterpret_cast<uptr>(Mprotect(kSpaceBeg, kSpaceSize)));
MapWithCallback(kSpaceEnd, AdditionalSize()); MapWithCallback(kSpaceEnd, AdditionalSize());
} }
@ -253,15 +255,20 @@ class SizeClassAllocator64 {
// Populate the free list with at most this number of bytes at once // Populate the free list with at most this number of bytes at once
// or with one element if its size is greater. // or with one element if its size is greater.
static const uptr kPopulateSize = 1 << 18; static const uptr kPopulateSize = 1 << 18;
// Call mmap for user memory with this size.
static const uptr kUserMapSize = 1 << 22;
// Call mmap for metadata memory with this size.
static const uptr kMetaMapSize = 1 << 20;
struct RegionInfo { struct RegionInfo {
SpinMutex mutex; SpinMutex mutex;
AllocatorFreeList free_list; AllocatorFreeList free_list;
uptr allocated_user; // Bytes allocated for user memory. uptr allocated_user; // Bytes allocated for user memory.
uptr allocated_meta; // Bytes allocated for metadata. uptr allocated_meta; // Bytes allocated for metadata.
char padding[kCacheLineSize - 3 * sizeof(uptr) - sizeof(AllocatorFreeList)]; uptr mapped_user; // Bytes mapped for user memory.
uptr mapped_meta; // Bytes mapped for metadata.
}; };
COMPILER_CHECK(sizeof(RegionInfo) == kCacheLineSize); COMPILER_CHECK(sizeof(RegionInfo) >= kCacheLineSize);
static uptr AdditionalSize() { static uptr AdditionalSize() {
uptr PageSize = GetPageSizeCached(); uptr PageSize = GetPageSizeCached();
@ -290,6 +297,12 @@ class SizeClassAllocator64 {
uptr beg_idx = region->allocated_user; uptr beg_idx = region->allocated_user;
uptr end_idx = beg_idx + kPopulateSize; uptr end_idx = beg_idx + kPopulateSize;
uptr region_beg = kSpaceBeg + kRegionSize * class_id; uptr region_beg = kSpaceBeg + kRegionSize * class_id;
if (end_idx > region->mapped_user) {
// Do the mmap for the user memory.
CHECK_GT(region->mapped_user + kUserMapSize, end_idx);
MapWithCallback(region_beg + region->mapped_user, kUserMapSize);
region->mapped_user += kUserMapSize;
}
uptr idx = beg_idx; uptr idx = beg_idx;
uptr i = 0; uptr i = 0;
do { // do-while loop because we need to put at least one item. do { // do-while loop because we need to put at least one item.
@ -300,6 +313,13 @@ class SizeClassAllocator64 {
} while (idx < end_idx); } while (idx < end_idx);
region->allocated_user += idx - beg_idx; region->allocated_user += idx - beg_idx;
region->allocated_meta += i * kMetadataSize; region->allocated_meta += i * kMetadataSize;
if (region->allocated_meta > region->mapped_meta) {
// Do the mmap for the metadata.
CHECK_GT(region->mapped_meta + kMetaMapSize, region->allocated_meta);
MapWithCallback(region_beg + kRegionSize -
region->mapped_meta - kMetaMapSize, kMetaMapSize);
region->mapped_meta += kMetaMapSize;
}
if (region->allocated_user + region->allocated_meta > kRegionSize) { if (region->allocated_user + region->allocated_meta > kRegionSize) {
Printf("Out of memory. Dying.\n"); Printf("Out of memory. Dying.\n");
Printf("The process has exhausted %zuMB for size class %zu.\n", Printf("The process has exhausted %zuMB for size class %zu.\n",

View File

@ -197,6 +197,8 @@ TEST(SanitizerCommon, SizeClassAllocator64MapUnmapCallback) {
Allocator64WithCallBack *a = new Allocator64WithCallBack; Allocator64WithCallBack *a = new Allocator64WithCallBack;
a->Init(); a->Init();
EXPECT_EQ(TestMapUnmapCallback::map_count, 1); // Allocator state. EXPECT_EQ(TestMapUnmapCallback::map_count, 1); // Allocator state.
a->Allocate(100, 1);
EXPECT_EQ(TestMapUnmapCallback::map_count, 3); // State + alloc + metadata.
a->TestOnlyUnmap(); a->TestOnlyUnmap();
EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); // The whole thing. EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); // The whole thing.
delete a; delete a;