Skip to content

Commit 8e8a06f

Browse files
ulanCommit Bot
authored andcommitted
[heap] Fix an out-of-bounds access in the marking bitmap
Deserializer can trigger OOB read in the marking bitmap inside the RegisterDeserializedObjectsForBlackAllocation function. This happens for example if an internalized string is deserialized as the last object on a page and is the turned into a thin-string leaving a one-word filler at the end of the page. In such a case IsBlack(filler) will try to fetch a cell outside the marking bitmap. The fix is to increase the size of the marking bitmap by one cell, so that it is always safe to query markbits of any object on a page. Bug: chromium:978156 Change-Id: If3c74e4f97d2caeb3c3f37a4147f38dea5f0e5a8 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2152838 Commit-Queue: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Dominik Inführ <dinfuehr@chromium.org> Cr-Commit-Position: refs/heads/master@{#67223}
1 parent c85aa83 commit 8e8a06f

4 files changed

Lines changed: 54 additions & 15 deletions

File tree

src/heap/marking.cc

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
namespace v8 {
88
namespace internal {
99

10+
const size_t Bitmap::kSize = Bitmap::CellsCount() * Bitmap::kBytesPerCell;
11+
1012
template <>
1113
bool ConcurrentBitmap<AccessMode::NON_ATOMIC>::AllBitsSetInRange(
1214
uint32_t start_index, uint32_t end_index) {
@@ -77,7 +79,7 @@ class CellPrinter {
7779
public:
7880
CellPrinter() : seq_start(0), seq_type(0), seq_length(0) {}
7981

80-
void Print(uint32_t pos, uint32_t cell) {
82+
void Print(size_t pos, uint32_t cell) {
8183
if (cell == seq_type) {
8284
seq_length++;
8385
return;
@@ -92,14 +94,14 @@ class CellPrinter {
9294
return;
9395
}
9496

95-
PrintF("%d: ", pos);
97+
PrintF("%zu: ", pos);
9698
PrintWord(cell);
9799
PrintF("\n");
98100
}
99101

100102
void Flush() {
101103
if (seq_length > 0) {
102-
PrintF("%d: %dx%d\n", seq_start, seq_type == 0 ? 0 : 1,
104+
PrintF("%zu: %dx%zu\n", seq_start, seq_type == 0 ? 0 : 1,
103105
seq_length * Bitmap::kBitsPerCell);
104106
seq_length = 0;
105107
}
@@ -108,17 +110,17 @@ class CellPrinter {
108110
static bool IsSeq(uint32_t cell) { return cell == 0 || cell == 0xFFFFFFFF; }
109111

110112
private:
111-
uint32_t seq_start;
113+
size_t seq_start;
112114
uint32_t seq_type;
113-
uint32_t seq_length;
115+
size_t seq_length;
114116
};
115117

116118
} // anonymous namespace
117119

118120
template <>
119121
void ConcurrentBitmap<AccessMode::NON_ATOMIC>::Print() {
120122
CellPrinter printer;
121-
for (int i = 0; i < CellsCount(); i++) {
123+
for (size_t i = 0; i < CellsCount(); i++) {
122124
printer.Print(i, cells()[i]);
123125
}
124126
printer.Flush();
@@ -127,7 +129,7 @@ void ConcurrentBitmap<AccessMode::NON_ATOMIC>::Print() {
127129

128130
template <>
129131
bool ConcurrentBitmap<AccessMode::NON_ATOMIC>::IsClean() {
130-
for (int i = 0; i < CellsCount(); i++) {
132+
for (size_t i = 0; i < CellsCount(); i++) {
131133
if (cells()[i] != 0) {
132134
return false;
133135
}

src/heap/marking.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,18 @@ class V8_EXPORT_PRIVATE Bitmap {
9898
static const uint32_t kBytesPerCell = kBitsPerCell / kBitsPerByte;
9999
static const uint32_t kBytesPerCellLog2 = kBitsPerCellLog2 - kBitsPerByteLog2;
100100

101-
static const size_t kLength = (1 << kPageSizeBits) >> (kTaggedSizeLog2);
102-
103-
static const size_t kSize = (1 << kPageSizeBits) >>
104-
(kTaggedSizeLog2 + kBitsPerByteLog2);
105-
106-
static int CellsForLength(int length) {
101+
// The length is the number of bits in this bitmap. (+1) accounts for
102+
// the case where the markbits are queried for a one-word filler at the
103+
// end of the page.
104+
static const size_t kLength = ((1 << kPageSizeBits) >> kTaggedSizeLog2) + 1;
105+
// The size of the bitmap in bytes is CellsCount() * kBytesPerCell.
106+
static const size_t kSize;
107+
108+
static constexpr size_t CellsForLength(int length) {
107109
return (length + kBitsPerCell - 1) >> kBitsPerCellLog2;
108110
}
109111

110-
int CellsCount() { return CellsForLength(kLength); }
112+
static constexpr size_t CellsCount() { return CellsForLength(kLength); }
111113

112114
V8_INLINE static uint32_t IndexToCell(uint32_t index) {
113115
return index >> kBitsPerCellLog2;

test/cctest/heap/test-heap.cc

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6933,6 +6933,41 @@ TEST(NoCodeRangeInJitlessMode) {
69336933
CcTest::i_isolate()->heap()->memory_allocator()->code_range().is_empty());
69346934
}
69356935

6936+
TEST(Regress978156) {
6937+
if (!FLAG_incremental_marking) return;
6938+
ManualGCScope manual_gc_scope;
6939+
CcTest::InitializeVM();
6940+
6941+
HandleScope handle_scope(CcTest::i_isolate());
6942+
Heap* heap = CcTest::i_isolate()->heap();
6943+
6944+
// 1. Ensure that the new space is empty.
6945+
CcTest::CollectGarbage(NEW_SPACE);
6946+
CcTest::CollectGarbage(NEW_SPACE);
6947+
// 2. Fill the first page of the new space with FixedArrays.
6948+
std::vector<Handle<FixedArray>> arrays;
6949+
i::heap::FillCurrentPage(heap->new_space(), &arrays);
6950+
// 3. Trim the last array by one word thus creating a one-word filler.
6951+
Handle<FixedArray> last = arrays.back();
6952+
CHECK_GT(last->length(), 0);
6953+
heap->RightTrimFixedArray(*last, 1);
6954+
// 4. Get the last filler on the page.
6955+
HeapObject filler = HeapObject::FromAddress(
6956+
MemoryChunk::FromHeapObject(*last)->area_end() - kTaggedSize);
6957+
HeapObject::FromAddress(last->address() + last->Size());
6958+
CHECK(filler.IsFiller());
6959+
// 5. Start incremental marking.
6960+
i::IncrementalMarking* marking = heap->incremental_marking();
6961+
if (marking->IsStopped()) {
6962+
marking->Start(i::GarbageCollectionReason::kTesting);
6963+
}
6964+
IncrementalMarking::MarkingState* marking_state = marking->marking_state();
6965+
// 6. Mark the filler black to access its two markbits. This triggers
6966+
// an out-of-bounds access of the marking bitmap in a bad case.
6967+
marking_state->WhiteToGrey(filler);
6968+
marking_state->GreyToBlack(filler);
6969+
}
6970+
69366971
} // namespace heap
69376972
} // namespace internal
69386973
} // namespace v8

test/unittests/heap/bitmap-unittest.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ TEST_F(NonAtomicBitmapTest, Cells) {
4141
}
4242

4343
TEST_F(NonAtomicBitmapTest, CellsCount) {
44-
int last_cell_index = bitmap()->CellsCount() - 1;
44+
size_t last_cell_index = bitmap()->CellsCount() - 1;
4545
bitmap()->cells()[last_cell_index] = kBlackCell;
4646
// Manually verify on raw memory.
4747
uint8_t* raw = raw_bitmap();

0 commit comments

Comments
 (0)