Skip to content

Commit

Permalink
Fix insecure internal API for GetImpl (facebook#8590)
Browse files Browse the repository at this point in the history
Summary:
Calling the GetImpl function could leave reference to a local
callback function in a field of a parameter struct. As this is
performance-critical code, I'm not going to attempt to sanitize this
code too much, but make the existing hack a bit cleaner by reverting
what it overwrites in the input struct.

Added SaveAndRestore utility class to make that easier.

Pull Request resolved: facebook#8590

Test Plan:
added unit test for SaveAndRestore; existing tests for
GetImpl

Reviewed By: riversand963

Differential Revision: D29947983

Pulled By: pdillinger

fbshipit-source-id: 2f608853f970bc06724e834cc84dcc4b8599ddeb
  • Loading branch information
pdillinger authored and Jaepil Jeong committed Aug 1, 2021
1 parent a96b161 commit 49fd1e9
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 1 deletion.
3 changes: 3 additions & 0 deletions db/db_impl/db_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
#include "util/coding.h"
#include "util/compression.h"
#include "util/crc32c.h"
#include "util/defer.h"
#include "util/mutexlock.h"
#include "util/stop_watch.h"
#include "util/string_util.h"
Expand Down Expand Up @@ -1736,6 +1737,8 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
}
// If timestamp is used, we use read callback to ensure <key,t,s> is returned
// only if t <= read_opts.timestamp and s <= snapshot.
// HACK: temporarily overwrite input struct field but restore
SaveAndRestore<ReadCallback*> restore_callback(&get_impl_options.callback);
if (ts_sz > 0) {
assert(!get_impl_options
.callback); // timestamp with callback is not supported
Expand Down
21 changes: 20 additions & 1 deletion util/defer.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ namespace ROCKSDB_NAMESPACE {
// lambda passed to Defer, and you can return immediately on failure when necessary.
class Defer final {
public:
Defer(std::function<void()>&& fn) : fn_(std::move(fn)) {}
explicit Defer(std::function<void()>&& fn) : fn_(std::move(fn)) {}
~Defer() { fn_(); }

// Disallow copy.
Expand All @@ -49,4 +49,23 @@ class Defer final {
std::function<void()> fn_;
};

// An RAII utility object that saves the current value of an object so that
// it can be overwritten, and restores it to the saved value when the
// SaveAndRestore object goes out of scope.
template <typename T>
class SaveAndRestore {
public:
// obj is non-null pointer to value to be saved and later restored.
explicit SaveAndRestore(T* obj) : obj_(obj), saved_(*obj) {}
~SaveAndRestore() { *obj_ = std::move(saved_); }

// No copies
SaveAndRestore(const SaveAndRestore&) = delete;
SaveAndRestore& operator=(const SaveAndRestore&) = delete;

private:
T* const obj_;
T saved_;
};

} // namespace ROCKSDB_NAMESPACE
11 changes: 11 additions & 0 deletions util/defer_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ TEST(DeferTest, FunctionScope) {
ASSERT_EQ(4, v);
}

TEST(SaveAndRestoreTest, BlockScope) {
int v = 1;
{
SaveAndRestore<int> sr(&v);
ASSERT_EQ(v, 1);
v = 2;
ASSERT_EQ(v, 2);
}
ASSERT_EQ(v, 1);
}

} // namespace ROCKSDB_NAMESPACE

int main(int argc, char** argv) {
Expand Down

0 comments on commit 49fd1e9

Please sign in to comment.