Fix use-after-free on implicit temporary FileOptions (#8571)

Summary:
FileOptions has an implicit conversion from EnvOptions and some
internal APIs take `const FileOptions&` and save the reference, which is
counter to Google C++ guidelines,

> Avoid defining functions that require a const reference parameter to outlive the call, because const reference parameters bind to temporaries. Instead, find a way to eliminate the lifetime requirement (for example, by copying the parameter), or pass it by const pointer and document the lifetime and non-null requirements.

This is at least a problem for repair.cc, which passes an EnvOptions to
TableCache(), which would save a reference to the temporary copy as
FileOptions. This was unfortunately only caught as a side effect of
changes in https://github.com/facebook/rocksdb/issues/8544.

This change fixes the repair.cc case and updates the involved internal
APIs that save a reference to use `const FileOptions*` instead.

Unfortunately, I don't know how to get any of our sanitizers to reliably
report bugs like this, so I can't rule out more existing in our
codebase.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/8571

Test Plan:
Test that issues seen with https://github.com/facebook/rocksdb/issues/8544 are fixed (can reproduce on
AWS EC2)

Reviewed By: ajkr

Differential Revision: D29943890

Pulled By: pdillinger

fbshipit-source-id: 95f9c5251548777b4dc994c1a083dd2add5799c9
main
Peter Dillinger 3 years ago committed by Facebook GitHub Bot
parent e352bd5742
commit 74b7c0d249
  1. 8
      db/column_family.cc
  2. 5
      db/column_family.h
  3. 19
      db/repair.cc
  4. 4
      db/table_cache.cc
  5. 2
      db/table_cache.h

@ -505,7 +505,7 @@ ColumnFamilyData::ColumnFamilyData(
uint32_t id, const std::string& name, Version* _dummy_versions, uint32_t id, const std::string& name, Version* _dummy_versions,
Cache* _table_cache, WriteBufferManager* write_buffer_manager, Cache* _table_cache, WriteBufferManager* write_buffer_manager,
const ColumnFamilyOptions& cf_options, const ImmutableDBOptions& db_options, const ColumnFamilyOptions& cf_options, const ImmutableDBOptions& db_options,
const FileOptions& file_options, ColumnFamilySet* column_family_set, const FileOptions* file_options, ColumnFamilySet* column_family_set,
BlockCacheTracer* const block_cache_tracer, BlockCacheTracer* const block_cache_tracer,
const std::shared_ptr<IOTracer>& io_tracer, const std::shared_ptr<IOTracer>& io_tracer,
const std::string& db_session_id) const std::string& db_session_id)
@ -1459,14 +1459,14 @@ ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
const std::shared_ptr<IOTracer>& io_tracer, const std::shared_ptr<IOTracer>& io_tracer,
const std::string& db_session_id) const std::string& db_session_id)
: max_column_family_(0), : max_column_family_(0),
file_options_(file_options),
dummy_cfd_(new ColumnFamilyData( dummy_cfd_(new ColumnFamilyData(
ColumnFamilyData::kDummyColumnFamilyDataId, "", nullptr, nullptr, ColumnFamilyData::kDummyColumnFamilyDataId, "", nullptr, nullptr,
nullptr, ColumnFamilyOptions(), *db_options, file_options, nullptr, nullptr, ColumnFamilyOptions(), *db_options, &file_options_, nullptr,
block_cache_tracer, io_tracer, db_session_id)), block_cache_tracer, io_tracer, db_session_id)),
default_cfd_cache_(nullptr), default_cfd_cache_(nullptr),
db_name_(dbname), db_name_(dbname),
db_options_(db_options), db_options_(db_options),
file_options_(file_options),
table_cache_(table_cache), table_cache_(table_cache),
write_buffer_manager_(_write_buffer_manager), write_buffer_manager_(_write_buffer_manager),
write_controller_(_write_controller), write_controller_(_write_controller),
@ -1538,7 +1538,7 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
assert(column_families_.find(name) == column_families_.end()); assert(column_families_.find(name) == column_families_.end());
ColumnFamilyData* new_cfd = new ColumnFamilyData( ColumnFamilyData* new_cfd = new ColumnFamilyData(
id, name, dummy_versions, table_cache_, write_buffer_manager_, options, id, name, dummy_versions, table_cache_, write_buffer_manager_, options,
*db_options_, file_options_, this, block_cache_tracer_, io_tracer_, *db_options_, &file_options_, this, block_cache_tracer_, io_tracer_,
db_session_id_); db_session_id_);
column_families_.insert({name, id}); column_families_.insert({name, id});
column_family_data_.insert({id, new_cfd}); column_family_data_.insert({id, new_cfd});

@ -530,7 +530,7 @@ class ColumnFamilyData {
WriteBufferManager* write_buffer_manager, WriteBufferManager* write_buffer_manager,
const ColumnFamilyOptions& options, const ColumnFamilyOptions& options,
const ImmutableDBOptions& db_options, const ImmutableDBOptions& db_options,
const FileOptions& file_options, const FileOptions* file_options,
ColumnFamilySet* column_family_set, ColumnFamilySet* column_family_set,
BlockCacheTracer* const block_cache_tracer, BlockCacheTracer* const block_cache_tracer,
const std::shared_ptr<IOTracer>& io_tracer, const std::shared_ptr<IOTracer>& io_tracer,
@ -721,6 +721,8 @@ class ColumnFamilySet {
std::unordered_map<uint32_t, ColumnFamilyData*> column_family_data_; std::unordered_map<uint32_t, ColumnFamilyData*> column_family_data_;
uint32_t max_column_family_; uint32_t max_column_family_;
const FileOptions file_options_;
ColumnFamilyData* dummy_cfd_; ColumnFamilyData* dummy_cfd_;
// We don't hold the refcount here, since default column family always exists // We don't hold the refcount here, since default column family always exists
// We are also not responsible for cleaning up default_cfd_cache_. This is // We are also not responsible for cleaning up default_cfd_cache_. This is
@ -730,7 +732,6 @@ class ColumnFamilySet {
const std::string db_name_; const std::string db_name_;
const ImmutableDBOptions* const db_options_; const ImmutableDBOptions* const db_options_;
const FileOptions file_options_;
Cache* table_cache_; Cache* table_cache_;
WriteBufferManager* write_buffer_manager_; WriteBufferManager* write_buffer_manager_;
WriteController* write_controller_; WriteController* write_controller_;

@ -94,7 +94,7 @@ class Repairer {
const ColumnFamilyOptions& unknown_cf_opts, bool create_unknown_cfs) const ColumnFamilyOptions& unknown_cf_opts, bool create_unknown_cfs)
: dbname_(dbname), : dbname_(dbname),
env_(db_options.env), env_(db_options.env),
env_options_(), file_options_(),
db_options_(SanitizeOptions(dbname_, db_options)), db_options_(SanitizeOptions(dbname_, db_options)),
immutable_db_options_(ImmutableDBOptions(db_options_)), immutable_db_options_(ImmutableDBOptions(db_options_)),
icmp_(default_cf_opts.comparator), icmp_(default_cf_opts.comparator),
@ -112,14 +112,15 @@ class Repairer {
table_cache_( table_cache_(
// TODO: db_session_id for TableCache should be initialized after // TODO: db_session_id for TableCache should be initialized after
// db_session_id_ is set. // db_session_id_ is set.
new TableCache(default_iopts_, env_options_, raw_table_cache_.get(), new TableCache(default_iopts_, &file_options_,
raw_table_cache_.get(),
/*block_cache_tracer=*/nullptr, /*block_cache_tracer=*/nullptr,
/*io_tracer=*/nullptr, /*db_session_id*/ "")), /*io_tracer=*/nullptr, /*db_session_id*/ "")),
wb_(db_options_.db_write_buffer_size), wb_(db_options_.db_write_buffer_size),
wc_(db_options_.delayed_write_rate), wc_(db_options_.delayed_write_rate),
// TODO: db_session_id for VersionSet should be initialized after // TODO: db_session_id for VersionSet should be initialized after
// db_session_id_ is set and use it for initialization. // db_session_id_ is set and use it for initialization.
vset_(dbname_, &immutable_db_options_, env_options_, vset_(dbname_, &immutable_db_options_, file_options_,
raw_table_cache_.get(), &wb_, &wc_, raw_table_cache_.get(), &wb_, &wc_,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr, /*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
/*db_session_id*/ ""), /*db_session_id*/ ""),
@ -249,7 +250,7 @@ class Repairer {
std::string const dbname_; std::string const dbname_;
std::string db_session_id_; std::string db_session_id_;
Env* const env_; Env* const env_;
const EnvOptions env_options_; const FileOptions file_options_;
const DBOptions db_options_; const DBOptions db_options_;
const ImmutableDBOptions immutable_db_options_; const ImmutableDBOptions immutable_db_options_;
const InternalKeyComparator icmp_; const InternalKeyComparator icmp_;
@ -366,7 +367,7 @@ class Repairer {
const auto& fs = env_->GetFileSystem(); const auto& fs = env_->GetFileSystem();
std::unique_ptr<SequentialFileReader> lfile_reader; std::unique_ptr<SequentialFileReader> lfile_reader;
Status status = SequentialFileReader::Create( Status status = SequentialFileReader::Create(
fs, logname, fs->OptimizeForLogRead(env_options_), &lfile_reader, fs, logname, fs->OptimizeForLogRead(file_options_), &lfile_reader,
nullptr); nullptr);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
@ -458,7 +459,7 @@ class Repairer {
meta.fd.GetNumber()); meta.fd.GetNumber());
status = BuildTable( status = BuildTable(
dbname_, /* versions */ nullptr, immutable_db_options_, tboptions, dbname_, /* versions */ nullptr, immutable_db_options_, tboptions,
env_options_, table_cache_.get(), iter.get(), file_options_, table_cache_.get(), iter.get(),
std::move(range_del_iters), &meta, nullptr /* blob_file_additions */, std::move(range_del_iters), &meta, nullptr /* blob_file_additions */,
{}, kMaxSequenceNumber, snapshot_checker, {}, kMaxSequenceNumber, snapshot_checker,
false /* paranoid_file_checks*/, nullptr /* internal_stats */, &io_s, false /* paranoid_file_checks*/, nullptr /* internal_stats */, &io_s,
@ -510,8 +511,8 @@ class Repairer {
file_size); file_size);
std::shared_ptr<const TableProperties> props; std::shared_ptr<const TableProperties> props;
if (status.ok()) { if (status.ok()) {
status = table_cache_->GetTableProperties(env_options_, icmp_, t->meta.fd, status = table_cache_->GetTableProperties(file_options_, icmp_,
&props); t->meta.fd, &props);
} }
if (status.ok()) { if (status.ok()) {
t->column_family_id = static_cast<uint32_t>(props->column_family_id); t->column_family_id = static_cast<uint32_t>(props->column_family_id);
@ -551,7 +552,7 @@ class Repairer {
ReadOptions ropts; ReadOptions ropts;
ropts.total_order_seek = true; ropts.total_order_seek = true;
InternalIterator* iter = table_cache_->NewIterator( InternalIterator* iter = table_cache_->NewIterator(
ropts, env_options_, cfd->internal_comparator(), t->meta, ropts, file_options_, cfd->internal_comparator(), t->meta,
nullptr /* range_del_agg */, nullptr /* range_del_agg */,
cfd->GetLatestMutableCFOptions()->prefix_extractor.get(), cfd->GetLatestMutableCFOptions()->prefix_extractor.get(),
/*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr, /*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,

@ -66,12 +66,12 @@ void AppendVarint64(IterKey* key, uint64_t v) {
const int kLoadConcurency = 128; const int kLoadConcurency = 128;
TableCache::TableCache(const ImmutableOptions& ioptions, TableCache::TableCache(const ImmutableOptions& ioptions,
const FileOptions& file_options, Cache* const cache, const FileOptions* file_options, Cache* const cache,
BlockCacheTracer* const block_cache_tracer, BlockCacheTracer* const block_cache_tracer,
const std::shared_ptr<IOTracer>& io_tracer, const std::shared_ptr<IOTracer>& io_tracer,
const std::string& db_session_id) const std::string& db_session_id)
: ioptions_(ioptions), : ioptions_(ioptions),
file_options_(file_options), file_options_(*file_options),
cache_(cache), cache_(cache),
immortal_tables_(false), immortal_tables_(false),
block_cache_tracer_(block_cache_tracer), block_cache_tracer_(block_cache_tracer),

@ -49,7 +49,7 @@ class HistogramImpl;
class TableCache { class TableCache {
public: public:
TableCache(const ImmutableOptions& ioptions, TableCache(const ImmutableOptions& ioptions,
const FileOptions& storage_options, Cache* cache, const FileOptions* storage_options, Cache* cache,
BlockCacheTracer* const block_cache_tracer, BlockCacheTracer* const block_cache_tracer,
const std::shared_ptr<IOTracer>& io_tracer, const std::shared_ptr<IOTracer>& io_tracer,
const std::string& db_session_id); const std::string& db_session_id);

Loading…
Cancel
Save