diff options
author | Cheng Chang <xcc@fb.com> | 2020-12-09 16:23:52 -0800 |
---|---|---|
committer | Facebook GitHub Bot <facebook-github-bot@users.noreply.github.com> | 2020-12-09 16:26:35 -0800 |
commit | 89cc06b3e73838832f9d9aa774afa0be42cfb599 (patch) | |
tree | 076ae8121767b8af68bcf33ed7e15e1efabba4ee /fuzz/db_map_fuzzer.cc | |
parent | efe827baf025e7c2fc544eba1b549e3ca013ddfa (diff) |
Add a new db_map_fuzzer (#7762)
Summary:
Execute randomly generated operations on both a DB and a std::map,
then reopen the DB and make sure that iterating the DB produces the
same key-value pairs as iterating through the std::map.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/7762
Test Plan: cd fuzz && make db_map_fuzzer && ./db_map_fuzzer
Reviewed By: pdillinger
Differential Revision: D25437485
Pulled By: cheng-chang
fbshipit-source-id: 3a93f7efd046b194193e45d2ab1ad81565510781
Diffstat (limited to 'fuzz/db_map_fuzzer.cc')
-rw-r--r-- | fuzz/db_map_fuzzer.cc | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/fuzz/db_map_fuzzer.cc b/fuzz/db_map_fuzzer.cc new file mode 100644 index 000000000..b9672018a --- /dev/null +++ b/fuzz/db_map_fuzzer.cc @@ -0,0 +1,121 @@ +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). + +#include <algorithm> +#include <iostream> +#include <map> +#include <string> + +#include "proto/gen/db_operation.pb.h" +#include "rocksdb/db.h" +#include "rocksdb/file_system.h" +#include "src/libfuzzer/libfuzzer_macro.h" + +#define CHECK_OK(expression) \ + do { \ + auto status = (expression); \ + if (!status.ok()) { \ + std::cerr << status.ToString() << std::endl; \ + abort(); \ + } \ + } while (0) + +#define CHECK_EQ(a, b) \ + if (a != b) { \ + std::cerr << "(" << #a << "=" << a << ") != (" << #b << "=" << b << ")" \ + << std::endl; \ + abort(); \ + } + +#define CHECK_TRUE(cond) \ + if (!(cond)) { \ + std::cerr << "\"" << #cond << "\" is false" << std::endl; \ + abort(); \ + } + +protobuf_mutator::libfuzzer::PostProcessorRegistration<DBOperations> reg = { + [](DBOperations* input, unsigned int /* seed */) { + const rocksdb::Comparator* comparator = rocksdb::BytewiseComparator(); + auto ops = input->mutable_operations(); + // Make sure begin <= end for DELETE_RANGE. + for (DBOperation& op : *ops) { + if (op.type() == OpType::DELETE_RANGE) { + auto begin = op.key(); + auto end = op.value(); + if (comparator->Compare(begin, end) > 0) { + std::swap(begin, end); + op.set_key(begin); + op.set_value(end); + } + } + } + }}; + +// Execute randomly generated operations on both a DB and a std::map, +// then reopen the DB and make sure that iterating the DB produces the +// same key-value pairs as iterating through the std::map. +DEFINE_PROTO_FUZZER(DBOperations& input) { + if (input.operations().empty()) { + return; + } + + const std::string kDbPath = "/tmp/db_map_fuzzer_test"; + auto fs = rocksdb::FileSystem::Default(); + if (fs->FileExists(kDbPath, rocksdb::IOOptions(), /*dbg=*/nullptr).ok()) { + std::cerr << "db path " << kDbPath << " already exists" << std::endl; + abort(); + } + + std::map<std::string, std::string> kv; + rocksdb::DB* db = nullptr; + rocksdb::Options options; + options.create_if_missing = true; + CHECK_OK(rocksdb::DB::Open(options, kDbPath, &db)); + + for (const DBOperation& op : input.operations()) { + switch (op.type()) { + case OpType::PUT: { + CHECK_OK(db->Put(rocksdb::WriteOptions(), op.key(), op.value())); + kv[op.key()] = op.value(); + break; + } + case OpType::DELETE: { + CHECK_OK(db->Delete(rocksdb::WriteOptions(), op.key())); + kv.erase(op.key()); + break; + } + case OpType::DELETE_RANGE: { + // [op.key(), op.value()) corresponds to [begin, end). + CHECK_OK(db->DeleteRange(rocksdb::WriteOptions(), + db->DefaultColumnFamily(), op.key(), + op.value())); + kv.erase(kv.lower_bound(op.key()), kv.lower_bound(op.value())); + break; + } + default: { + std::cerr << "Unsupported operation" << static_cast<int>(op.type()); + return; + } + } + } + CHECK_OK(db->Close()); + delete db; + db = nullptr; + + CHECK_OK(rocksdb::DB::Open(options, kDbPath, &db)); + auto kv_it = kv.begin(); + rocksdb::Iterator* it = db->NewIterator(rocksdb::ReadOptions()); + for (it->SeekToFirst(); it->Valid(); it->Next(), kv_it++) { + CHECK_TRUE(kv_it != kv.end()); + CHECK_EQ(it->key().ToString(), kv_it->first); + CHECK_EQ(it->value().ToString(), kv_it->second); + } + CHECK_TRUE(kv_it == kv.end()); + delete it; + + CHECK_OK(db->Close()); + delete db; + CHECK_OK(rocksdb::DestroyDB(kDbPath, options)); +} |