699411b2ca
Summary: This is the initial PR to support adding fuzz tests to RocksDB. It includes the necessary build infrastructure, and includes an example fuzzer. There is also a README serving as the tutorial for how to add more tests. Pull Request resolved: https://github.com/facebook/rocksdb/pull/7685 Test Plan: Manually build and run the fuzz test according to README. Reviewed By: pdillinger Differential Revision: D25013847 Pulled By: cheng-chang fbshipit-source-id: c91e3b337398d7f4d8f769fd5091cd080487b171
102 lines
3.2 KiB
C++
102 lines
3.2 KiB
C++
// 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 "proto/gen/db_operation.pb.h"
|
|
#include "rocksdb/file_system.h"
|
|
#include "rocksdb/sst_file_reader.h"
|
|
#include "rocksdb/sst_file_writer.h"
|
|
#include "src/libfuzzer/libfuzzer_macro.h"
|
|
|
|
// Keys in SST file writer operations must be unique and in ascending order.
|
|
// For each DBOperation generated by the fuzzer, this function is called on
|
|
// it to deduplicate and sort the keys in the DBOperations.
|
|
protobuf_mutator::libfuzzer::PostProcessorRegistration<DBOperations> reg = {
|
|
[](DBOperations* input, unsigned int /* seed */) {
|
|
const rocksdb::Comparator* comparator = rocksdb::BytewiseComparator();
|
|
auto ops = input->mutable_operations();
|
|
std::sort(ops->begin(), ops->end(),
|
|
[&comparator](const DBOperation& a, const DBOperation& b) {
|
|
return comparator->Compare(a.key(), b.key()) < 0;
|
|
});
|
|
|
|
auto last = std::unique(
|
|
ops->begin(), ops->end(),
|
|
[&comparator](const DBOperation& a, const DBOperation& b) {
|
|
return comparator->Compare(a.key(), b.key()) == 0;
|
|
});
|
|
ops->erase(last, ops->end());
|
|
}};
|
|
|
|
#define CHECK_OK(status) \
|
|
if (!status.ok()) { \
|
|
std::cerr << status.ToString() << std::endl; \
|
|
abort(); \
|
|
}
|
|
|
|
// Fuzzes DB operations as input, let SstFileWriter generate a SST file
|
|
// according to the operations, then let SstFileReader read the generated SST
|
|
// file to check its checksum.
|
|
DEFINE_PROTO_FUZZER(DBOperations& input) {
|
|
if (input.operations().empty()) {
|
|
return;
|
|
}
|
|
|
|
std::string sstfile;
|
|
{
|
|
auto fs = rocksdb::FileSystem::Default();
|
|
std::string dir;
|
|
rocksdb::IOOptions opt;
|
|
rocksdb::IOStatus s = fs->GetTestDirectory(opt, &dir, nullptr);
|
|
CHECK_OK(s);
|
|
sstfile = dir + "/SstFileWriterFuzzer.sst";
|
|
}
|
|
|
|
// Generate sst file.
|
|
rocksdb::Options options;
|
|
rocksdb::EnvOptions env_options;
|
|
rocksdb::SstFileWriter writer(env_options, options);
|
|
rocksdb::Status s = writer.Open(sstfile);
|
|
CHECK_OK(s);
|
|
for (const DBOperation& op : input.operations()) {
|
|
switch (op.type()) {
|
|
case OpType::PUT:
|
|
s = writer.Put(op.key(), op.value());
|
|
CHECK_OK(s);
|
|
break;
|
|
case OpType::MERGE:
|
|
s = writer.Merge(op.key(), op.value());
|
|
CHECK_OK(s);
|
|
break;
|
|
case OpType::DELETE:
|
|
s = writer.Delete(op.key());
|
|
CHECK_OK(s);
|
|
break;
|
|
case OpType::DELETE_RANGE:
|
|
s = writer.DeleteRange(op.key(), op.value());
|
|
CHECK_OK(s);
|
|
break;
|
|
default:
|
|
std::cerr << "Unsupported operation" << static_cast<int>(op.type());
|
|
return;
|
|
}
|
|
}
|
|
rocksdb::ExternalSstFileInfo info;
|
|
s = writer.Finish(&info);
|
|
CHECK_OK(s);
|
|
|
|
// Verify checksum.
|
|
rocksdb::SstFileReader reader(options);
|
|
s = reader.Open(sstfile);
|
|
CHECK_OK(s);
|
|
s = reader.VerifyChecksum();
|
|
CHECK_OK(s);
|
|
|
|
// Delete sst file.
|
|
remove(sstfile.c_str());
|
|
}
|