Fix Forward Iterator Seek()/SeekToFirst()
Summary: In ForwardIterator::SeekInternal(), we may end up passing empty Slice representing an internal key to InternalKeyComparator::Compare. and when we try to extract the user key from this empty Slice, we will create a slice with size = 0 - 8 ( which will overflow and cause us to read invalid memory as well ) Scenarios to reproduce these issues are in the unit tests Closes https://github.com/facebook/rocksdb/pull/1467 Differential Revision: D4136660 Pulled By: lightmark fbshipit-source-id: 151e128
This commit is contained in:
parent
e48f3f8b9e
commit
193221e0a1
@ -752,6 +752,58 @@ TEST_F(DBTestTailingIterator, ForwardIteratorVersionProperty) {
|
|||||||
}
|
}
|
||||||
ASSERT_EQ(v3, v4);
|
ASSERT_EQ(v3, v4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DBTestTailingIterator, SeekWithUpperBoundBug) {
|
||||||
|
ReadOptions read_options;
|
||||||
|
read_options.tailing = true;
|
||||||
|
const Slice upper_bound("cc", 3);
|
||||||
|
read_options.iterate_upper_bound = &upper_bound;
|
||||||
|
|
||||||
|
|
||||||
|
// 1st L0 file
|
||||||
|
ASSERT_OK(db_->Put(WriteOptions(), "aa", "SEEN"));
|
||||||
|
ASSERT_OK(Flush());
|
||||||
|
|
||||||
|
// 2nd L0 file
|
||||||
|
ASSERT_OK(db_->Put(WriteOptions(), "zz", "NOT-SEEN"));
|
||||||
|
ASSERT_OK(Flush());
|
||||||
|
|
||||||
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||||
|
|
||||||
|
iter->Seek("aa");
|
||||||
|
ASSERT_TRUE(iter->Valid());
|
||||||
|
ASSERT_EQ(iter->key().ToString(), "aa");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DBTestTailingIterator, SeekToFirstWithUpperBoundBug) {
|
||||||
|
ReadOptions read_options;
|
||||||
|
read_options.tailing = true;
|
||||||
|
const Slice upper_bound("cc", 3);
|
||||||
|
read_options.iterate_upper_bound = &upper_bound;
|
||||||
|
|
||||||
|
|
||||||
|
// 1st L0 file
|
||||||
|
ASSERT_OK(db_->Put(WriteOptions(), "aa", "SEEN"));
|
||||||
|
ASSERT_OK(Flush());
|
||||||
|
|
||||||
|
// 2nd L0 file
|
||||||
|
ASSERT_OK(db_->Put(WriteOptions(), "zz", "NOT-SEEN"));
|
||||||
|
ASSERT_OK(Flush());
|
||||||
|
|
||||||
|
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||||
|
|
||||||
|
iter->SeekToFirst();
|
||||||
|
ASSERT_TRUE(iter->Valid());
|
||||||
|
ASSERT_EQ(iter->key().ToString(), "aa");
|
||||||
|
|
||||||
|
iter->Next();
|
||||||
|
ASSERT_FALSE(iter->Valid());
|
||||||
|
|
||||||
|
iter->SeekToFirst();
|
||||||
|
ASSERT_TRUE(iter->Valid());
|
||||||
|
ASSERT_EQ(iter->key().ToString(), "aa");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace rocksdb
|
} // namespace rocksdb
|
||||||
|
|
||||||
#endif // !defined(ROCKSDB_LITE)
|
#endif // !defined(ROCKSDB_LITE)
|
||||||
|
@ -296,9 +296,15 @@ void ForwardIterator::SeekInternal(const Slice& internal_key,
|
|||||||
// an option to turn it off.
|
// an option to turn it off.
|
||||||
if (seek_to_first || NeedToSeekImmutable(internal_key)) {
|
if (seek_to_first || NeedToSeekImmutable(internal_key)) {
|
||||||
immutable_status_ = Status::OK();
|
immutable_status_ = Status::OK();
|
||||||
if ((has_iter_trimmed_for_upper_bound_) &&
|
if (has_iter_trimmed_for_upper_bound_ &&
|
||||||
(cfd_->internal_comparator().InternalKeyComparator::Compare(
|
(
|
||||||
prev_key_.GetKey(), internal_key) > 0)) {
|
// prev_ is not set yet
|
||||||
|
is_prev_set_ == false ||
|
||||||
|
// We are doing SeekToFirst() and internal_key.size() = 0
|
||||||
|
seek_to_first ||
|
||||||
|
// prev_key_ > internal_key
|
||||||
|
cfd_->internal_comparator().InternalKeyComparator::Compare(
|
||||||
|
prev_key_.GetKey(), internal_key) > 0)) {
|
||||||
// Some iterators are trimmed. Need to rebuild.
|
// Some iterators are trimmed. Need to rebuild.
|
||||||
RebuildIterators(true);
|
RebuildIterators(true);
|
||||||
// Already seeked mutable iter, so seek again
|
// Already seeked mutable iter, so seek again
|
||||||
|
Loading…
Reference in New Issue
Block a user