From 58560a05ad15cd7439183e07e72bbb6b09d1f236 Mon Sep 17 00:00:00 2001 From: Kostya Serebryany Date: Fri, 9 Sep 2016 21:42:33 +0000 Subject: [PATCH] [sanitizer] fix an allocator bug where the allocated memory may overlap with the free array (kudos to Kostya Korcthinsky). Also make sure that the allocator does not mmap more than requested. Test both. llvm-svn: 281103 --- .../sanitizer_common/sanitizer_allocator_primary64.h | 7 ++++--- .../tests/sanitizer_allocator_test.cc | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_primary64.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_primary64.h index d6fdcd7..823af7d 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_primary64.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_primary64.h @@ -401,12 +401,12 @@ class SizeClassAllocator64 { uptr beg_idx = region->allocated_user; uptr end_idx = beg_idx + requested_count * size; uptr region_beg = GetRegionBeginBySizeClass(class_id); - if (end_idx + size > region->mapped_user) { + if (end_idx > region->mapped_user) { if (!kUsingConstantSpaceBeg && region->mapped_user == 0) region->rand_state = static_cast(region_beg >> 12); // From ASLR. // Do the mmap for the user memory. uptr map_size = kUserMapSize; - while (end_idx + size > region->mapped_user + map_size) + while (end_idx > region->mapped_user + map_size) map_size += kUserMapSize; CHECK_GE(region->mapped_user + map_size, end_idx); MapWithCallback(region_beg + region->mapped_user, map_size); @@ -441,7 +441,8 @@ class SizeClassAllocator64 { region->mapped_meta += map_size; } CHECK_LE(region->allocated_meta, region->mapped_meta); - if (region->mapped_user + region->mapped_meta > kRegionSize) { + if (region->mapped_user + region->mapped_meta > + kRegionSize - kFreeArraySize) { Printf("%s: Out of memory. Dying. ", SanitizerToolName); Printf("The process has exhausted %zuMB for size class %zu.\n", kRegionSize / 1024 / 1024, size); diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc b/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc index 30e4aa5..4b5362a 100644 --- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc @@ -931,16 +931,33 @@ TEST(SanitizerCommon, SizeClassAllocator64PopulateFreeListOOM) { // ...one man is on a mission to overflow a region with a series of // successive allocations. + const uptr kClassID = 107; - const uptr kAllocationSize = DefaultSizeClassMap::Size(kClassID); + const uptr kAllocationSize = SpecialSizeClassMap::Size(kClassID); ASSERT_LT(2 * kAllocationSize, kRegionSize); ASSERT_GT(3 * kAllocationSize, kRegionSize); cache.Allocate(a, kClassID); EXPECT_DEATH(cache.Allocate(a, kClassID) && cache.Allocate(a, kClassID), "The process has exhausted"); + + const uptr Class2 = 100; + const uptr Size2 = SpecialSizeClassMap::Size(Class2); + ASSERT_EQ(Size2 * 8, kRegionSize); + char *p[7]; + for (int i = 0; i < 7; i++) { + p[i] = (char*)cache.Allocate(a, Class2); + fprintf(stderr, "p[%d] %p s = %lx\n", i, (void*)p[i], Size2); + p[i][Size2 - 1] = 42; + if (i) ASSERT_LT(p[i - 1], p[i]); + } + EXPECT_DEATH(cache.Allocate(a, Class2), "The process has exhausted"); + cache.Deallocate(a, Class2, p[0]); + cache.Drain(a); + ASSERT_EQ(p[6][Size2 - 1], 42); a->TestOnlyUnmap(); delete a; } + #endif TEST(SanitizerCommon, TwoLevelByteMap) { -- 2.7.4