From aa53579d6cc5a53c1493bbda4f21c0e9831b377b Mon Sep 17 00:00:00 2001 From: Yanqin Jin Date: Fri, 25 May 2018 11:45:12 -0700 Subject: [PATCH] Fix segfault caused by object premature destruction Summary: Please refer to earlier discussion in [issue 3609](https://github.com/facebook/rocksdb/issues/3609). There was also an alternative fix in [PR 3888](https://github.com/facebook/rocksdb/pull/3888), but the proposed solution requires complex change. To summarize the cause of the problem. Upon creation of a column family, a `BlockBasedTableFactory` object is `new`ed and encapsulated by a `std::shared_ptr`. Since there is no other `std::shared_ptr` pointing to this `BlockBasedTableFactory`, when the column family is dropped, the `ColumnFamilyData` is `delete`d, causing the destructor of `std::shared_ptr`. Since there is no other `std::shared_ptr`, the underlying memory is also freed. Later when the db exits, it releases all the table readers, including the table readers that have been operating on the dropped column family. This needs to access the `table_options` owned by `BlockBasedTableFactory` that has already been deleted. Therefore, a segfault is raised. Previous workaround is to purge all obsolete files upon `ColumnFamilyData` destruction, which leads to a force release of table readers of the dropped column family. However this does not work when the user disables file deletion. Our solution in this PR is making a copy of `table_options` in `BlockBasedTable::Rep`. This solution increases memory copy and usage, but is much simpler. Test plan ``` $ make -j16 $ ./column_family_test --gtest_filter=ColumnFamilyTest.CreateDropAndDestroy:ColumnFamilyTest.CreateDropAndDestroyWithoutFileDeletion ``` Expected behavior: All tests should pass. Closes https://github.com/facebook/rocksdb/pull/3898 Differential Revision: D8149421 Pulled By: riversand963 fbshipit-source-id: eaecc2e064057ef607fbdd4cc275874f866c3438 --- db/column_family_test.cc | 21 +++++++++++++++++++++ table/block_based_table_reader.h | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/db/column_family_test.cc b/db/column_family_test.cc index 93dc771ad..1d5619995 100644 --- a/db/column_family_test.cc +++ b/db/column_family_test.cc @@ -2827,6 +2827,27 @@ TEST_F(ColumnFamilyTest, CreateAndDestoryOptions) { ASSERT_OK(db_->DestroyColumnFamilyHandle(cfh)); } +TEST_F(ColumnFamilyTest, CreateDropAndDestroy) { + ColumnFamilyHandle* cfh; + Open(); + ASSERT_OK(db_->CreateColumnFamily(ColumnFamilyOptions(), "yoyo", &cfh)); + ASSERT_OK(db_->Put(WriteOptions(), cfh, "foo", "bar")); + ASSERT_OK(db_->Flush(FlushOptions(), cfh)); + ASSERT_OK(db_->DropColumnFamily(cfh)); + ASSERT_OK(db_->DestroyColumnFamilyHandle(cfh)); +} + +TEST_F(ColumnFamilyTest, CreateDropAndDestroyWithoutFileDeletion) { + ColumnFamilyHandle* cfh; + Open(); + ASSERT_OK(db_->CreateColumnFamily(ColumnFamilyOptions(), "yoyo", &cfh)); + ASSERT_OK(db_->Put(WriteOptions(), cfh, "foo", "bar")); + ASSERT_OK(db_->Flush(FlushOptions(), cfh)); + ASSERT_OK(db_->DisableFileDeletions()); + ASSERT_OK(db_->DropColumnFamily(cfh)); + ASSERT_OK(db_->DestroyColumnFamilyHandle(cfh)); +} + #ifndef ROCKSDB_LITE TEST_F(ColumnFamilyTest, FlushCloseWALFiles) { SpecialEnv env(Env::Default()); diff --git a/table/block_based_table_reader.h b/table/block_based_table_reader.h index 0b58c84e1..4baed2a60 100644 --- a/table/block_based_table_reader.h +++ b/table/block_based_table_reader.h @@ -429,7 +429,7 @@ struct BlockBasedTable::Rep { const ImmutableCFOptions& ioptions; const EnvOptions& env_options; - const BlockBasedTableOptions& table_options; + const BlockBasedTableOptions table_options; const FilterPolicy* const filter_policy; const InternalKeyComparator& internal_comparator; Status status;