Skip to content

Commit 069337b

Browse files
wybDanRoscigno
authored andcommitted
[BugFix] Fix BE crash when child iterator is exhausted in MaskMergeIterator (StarRocks#70539)
Signed-off-by: wyb <wybb86@gmail.com>
1 parent a431427 commit 069337b

2 files changed

Lines changed: 43 additions & 0 deletions

File tree

be/src/storage/merge_iterator.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,10 @@ inline Status MaskMergeIterator::do_get_next(Chunk* chunk, std::vector<RowSource
497497
RowSourceMask mask = _mask_buffer->current();
498498
uint16_t child = mask.get_source_num();
499499
auto& min_chunk = _chunks[child];
500+
if (min_chunk._chunk == nullptr) {
501+
return Status::InternalError(strings::Substitute(
502+
"Mask buffer expects more rows from child $0, but child iterator is exhausted", child));
503+
}
500504
DCHECK_GT(min_chunk.remaining_rows(), 0);
501505

502506
size_t offset = min_chunk.compared_row();
@@ -584,8 +588,10 @@ inline Status MaskMergeIterator::fill(size_t child) {
584588
} else if (st.is_end_of_file()) {
585589
// ignore Status::EndOfFile.
586590
close_child(child);
591+
_chunks[child]._chunk = nullptr;
587592
} else {
588593
close_child(child);
594+
_chunks[child]._chunk = nullptr;
589595
return st;
590596
}
591597
return Status::OK();

be/test/storage/merge_iterator_test.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,4 +367,41 @@ TEST_F(MergeIteratorTest, mask_merge_boundary_test) {
367367
}
368368
}
369369

370+
TEST_F(MergeIteratorTest, mask_merge_exhausted_iterator) {
371+
std::vector<int32_t> v1{1, 2}; // Only 2 elements
372+
std::vector<int32_t> v2{10, 11};
373+
auto sub1 = std::make_shared<VectorChunkIterator>(_schema, COL_INT(v1));
374+
auto sub2 = std::make_shared<VectorChunkIterator>(_schema, COL_INT(v2));
375+
376+
std::vector<RowSourceMask> source_masks;
377+
// Request 3 elements from source 0, but it only has 2 elements.
378+
// This matches the exhausted iterator scenario that caused nullptr dereference.
379+
std::vector<uint16_t> expected_sources{0, 0, 0, 1, 1};
380+
for (unsigned short expected_source : expected_sources) {
381+
source_masks.emplace_back(RowSourceMask(expected_source, false));
382+
}
383+
RowSourceMaskBuffer mask_buffer(0, config::storage_root_path);
384+
mask_buffer.write(source_masks);
385+
mask_buffer.flush();
386+
mask_buffer.flip_to_read();
387+
source_masks.clear();
388+
389+
auto iter = new_mask_merge_iterator(std::vector<ChunkIteratorPtr>{sub1, sub2}, &mask_buffer);
390+
ASSERT_TRUE(iter->init_encoded_schema(EMPTY_GLOBAL_DICTMAPS).ok());
391+
392+
ChunkPtr chunk = ChunkHelper::new_chunk(iter->schema(), config::vector_chunk_size);
393+
Status st;
394+
while (true) {
395+
chunk->reset();
396+
st = iter->get_next(chunk.get(), &source_masks);
397+
if (!st.ok()) {
398+
break;
399+
}
400+
}
401+
// Should return InternalError instead of crashing
402+
ASSERT_FALSE(st.ok());
403+
ASSERT_TRUE(st.is_internal_error());
404+
ASSERT_TRUE(st.message().find("child iterator is exhausted") != std::string::npos);
405+
}
406+
370407
} // namespace starrocks

0 commit comments

Comments
 (0)