Fix insecure internal API for GetImpl (#8590)

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: https://github.com/facebook/rocksdb/pull/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
This commit is contained in:
Peter Dillinger 2021-07-29 14:58:35 -07:00 committed by Facebook GitHub Bot
parent 9501279d5f
commit 1d34cd797e
3 changed files with 34 additions and 1 deletions

View File

@ -99,6 +99,7 @@
#include "util/coding.h" #include "util/coding.h"
#include "util/compression.h" #include "util/compression.h"
#include "util/crc32c.h" #include "util/crc32c.h"
#include "util/defer.h"
#include "util/mutexlock.h" #include "util/mutexlock.h"
#include "util/stop_watch.h" #include "util/stop_watch.h"
#include "util/string_util.h" #include "util/string_util.h"
@ -1794,6 +1795,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 // If timestamp is used, we use read callback to ensure <key,t,s> is returned
// only if t <= read_opts.timestamp and s <= snapshot. // 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) { if (ts_sz > 0) {
assert(!get_impl_options assert(!get_impl_options
.callback); // timestamp with callback is not supported .callback); // timestamp with callback is not supported

View File

@ -38,7 +38,7 @@ namespace ROCKSDB_NAMESPACE {
// lambda passed to Defer, and you can return immediately on failure when necessary. // lambda passed to Defer, and you can return immediately on failure when necessary.
class Defer final { class Defer final {
public: public:
Defer(std::function<void()>&& fn) : fn_(std::move(fn)) {} explicit Defer(std::function<void()>&& fn) : fn_(std::move(fn)) {}
~Defer() { fn_(); } ~Defer() { fn_(); }
// Disallow copy. // Disallow copy.
@ -49,4 +49,23 @@ class Defer final {
std::function<void()> fn_; 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 } // namespace ROCKSDB_NAMESPACE

View File

@ -30,6 +30,17 @@ TEST(DeferTest, FunctionScope) {
ASSERT_EQ(4, v); 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 } // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) { int main(int argc, char** argv) {