From 9bc3d1a8fe3d4a1b32979b24b45d3f9cd5395339 Mon Sep 17 00:00:00 2001 From: "hpayer@chromium.org" Date: Tue, 1 Jul 2014 18:48:02 +0000 Subject: [PATCH] Added a promotion queue unit test that test promotion queue memory corruption by semi-space evacuation. BUG= R=titzer@chromium.org Review URL: https://codereview.chromium.org/362723003 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22134 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/heap.cc | 13 +++++++++ src/heap.h | 12 ++++++++ test/cctest/test-heap.cc | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/src/heap.cc b/src/heap.cc index 3d97974..ad962cd 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -1950,6 +1950,19 @@ class ScavengingVisitor : public StaticVisitorBase { HeapObject* source, HeapObject* target, int size)) { + // If we migrate into to-space, then the to-space top pointer should be + // right after the target object. Incorporate double alignment + // over-allocation. + ASSERT(!heap->InToSpace(target) || + target->address() + size == heap->new_space()->top() || + target->address() + size + kPointerSize == heap->new_space()->top()); + + // Make sure that we do not overwrite the promotion queue which is at + // the end of to-space. + ASSERT(!heap->InToSpace(target) || + heap->promotion_queue()->IsBelowPromotionQueue( + heap->new_space()->top())); + // Copy the content of source to target. heap->CopyBlock(target->address(), source->address(), size); diff --git a/src/heap.h b/src/heap.h index f3b02ce..f93f9c0 100644 --- a/src/heap.h +++ b/src/heap.h @@ -427,6 +427,18 @@ class PromotionQueue { RelocateQueueHead(); } + bool IsBelowPromotionQueue(Address to_space_top) { + // If the given to-space top pointer and the head of the promotion queue + // are not on the same page, then the to-space objects are below the + // promotion queue. + if (GetHeadPage() != Page::FromAddress(to_space_top)) { + return true; + } + // If the to space top pointer is smaller or equal than the promotion + // queue head, then the to-space objects are below the promotion queue. + return reinterpret_cast(to_space_top) <= rear_; + } + bool is_empty() { return (front_ == rear_) && (emergency_stack_ == NULL || emergency_stack_->length() == 0); diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc index f0682b2..5d61524 100644 --- a/test/cctest/test-heap.cc +++ b/test/cctest/test-heap.cc @@ -4329,6 +4329,81 @@ TEST(ArrayShiftSweeping) { } +TEST(PromotionQueue) { + i::FLAG_expose_gc = true; + i::FLAG_max_semi_space_size = 2; + CcTest::InitializeVM(); + v8::HandleScope scope(CcTest::isolate()); + Isolate* isolate = CcTest::i_isolate(); + Heap* heap = isolate->heap(); + NewSpace* new_space = heap->new_space(); + + // In this test we will try to overwrite the promotion queue which is at the + // end of to-space. To actually make that possible, we need at least two + // semi-space pages and take advantage of fragementation. + // (1) Grow semi-space to two pages. + // (2) Create a few small long living objects and call the scavenger to + // move them to the other semi-space. + // (3) Create a huge object, i.e., remainder of first semi-space page and + // create another huge object which should be of maximum allocatable memory + // size of the second semi-space page. + // (4) Call the scavenger again. + // What will happen is: the scavenger will promote the objects created in (2) + // and will create promotion queue entries at the end of the second + // semi-space page during the next scavenge when it promotes the objects to + // the old generation. The first allocation of (3) will fill up the first + // semi-space page. The second allocation in (3) will not fit into the first + // semi-space page, but it will overwrite the promotion queue which are in + // the second semi-space page. If the right guards are in place, the promotion + // queue will be evacuated in that case. + + // Grow the semi-space to two pages to make semi-space copy overwrite the + // promotion queue, which will be at the end of the second page. + intptr_t old_capacity = new_space->Capacity(); + new_space->Grow(); + CHECK(new_space->IsAtMaximumCapacity()); + CHECK(2 * old_capacity == new_space->Capacity()); + + // Call the scavenger two times to get an empty new space + heap->CollectGarbage(NEW_SPACE); + heap->CollectGarbage(NEW_SPACE); + + // First create a few objects which will survive a scavenge, and will get + // promoted to the old generation later on. These objects will create + // promotion queue entries at the end of the second semi-space page. + const int number_handles = 12; + Handle handles[number_handles]; + for (int i = 0; i < number_handles; i++) { + handles[i] = isolate->factory()->NewFixedArray(1, NOT_TENURED); + } + heap->CollectGarbage(NEW_SPACE); + + // Create the first huge object which will exactly fit the first semi-space + // page. + int new_linear_size = static_cast( + *heap->new_space()->allocation_limit_address() - + *heap->new_space()->allocation_top_address()); + int length = new_linear_size / kPointerSize - FixedArray::kHeaderSize; + Handle first = + isolate->factory()->NewFixedArray(length, NOT_TENURED); + CHECK(heap->InNewSpace(*first)); + + // Create the second huge object of maximum allocatable second semi-space + // page size. + new_linear_size = static_cast( + *heap->new_space()->allocation_limit_address() - + *heap->new_space()->allocation_top_address()); + length = Page::kMaxRegularHeapObjectSize / kPointerSize - + FixedArray::kHeaderSize; + Handle second = + isolate->factory()->NewFixedArray(length, NOT_TENURED); + CHECK(heap->InNewSpace(*second)); + + // This scavenge will corrupt memory if the promotion queue is not evacuated. + heap->CollectGarbage(NEW_SPACE); +} + + #ifdef DEBUG TEST(PathTracer) { CcTest::InitializeVM(); -- 2.7.4