summaryrefslogtreecommitdiffstats
path: root/src/rocksdb/fuzz/db_fuzzer.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/rocksdb/fuzz/db_fuzzer.cc172
1 files changed, 172 insertions, 0 deletions
diff --git a/src/rocksdb/fuzz/db_fuzzer.cc b/src/rocksdb/fuzz/db_fuzzer.cc
new file mode 100644
index 000000000..e6d5bb63c
--- /dev/null
+++ b/src/rocksdb/fuzz/db_fuzzer.cc
@@ -0,0 +1,172 @@
+// Copyright (c) Meta Platforms, Inc. and affiliates.
+//
+// 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 <fuzzer/FuzzedDataProvider.h>
+
+#include "rocksdb/db.h"
+
+enum OperationType {
+ kPut,
+ kGet,
+ kDelete,
+ kGetProperty,
+ kIterator,
+ kSnapshot,
+ kOpenClose,
+ kColumn,
+ kCompactRange,
+ kSeekForPrev,
+ OP_COUNT
+};
+
+constexpr char db_path[] = "/tmp/testdb";
+
+// Fuzzes DB operations by doing interpretations on the data. Both the
+// sequence of API calls to be called on the DB as well as the arguments
+// to each of these APIs are interpreted by way of the data buffer.
+// The operations that the fuzzer supports are given by the OperationType
+// enum. The goal is to capture sanitizer bugs, so the code should be
+// compiled with a given sanitizer (ASan, UBSan, MSan).
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ ROCKSDB_NAMESPACE::DB* db;
+ ROCKSDB_NAMESPACE::Options options;
+ options.create_if_missing = true;
+ ROCKSDB_NAMESPACE::Status status =
+ ROCKSDB_NAMESPACE::DB::Open(options, db_path, &db);
+ if (!status.ok()) {
+ return 0;
+ }
+ FuzzedDataProvider fuzzed_data(data, size);
+
+ // perform a sequence of calls on our db instance
+ int max_iter = static_cast<int>(data[0]);
+ for (int i = 0; i < max_iter && i < size; i++) {
+ OperationType op = static_cast<OperationType>(data[i] % OP_COUNT);
+
+ switch (op) {
+ case kPut: {
+ std::string key = fuzzed_data.ConsumeRandomLengthString();
+ std::string val = fuzzed_data.ConsumeRandomLengthString();
+ db->Put(ROCKSDB_NAMESPACE::WriteOptions(), key, val);
+ break;
+ }
+ case kGet: {
+ std::string key = fuzzed_data.ConsumeRandomLengthString();
+ std::string value;
+ db->Get(ROCKSDB_NAMESPACE::ReadOptions(), key, &value);
+ break;
+ }
+ case kDelete: {
+ std::string key = fuzzed_data.ConsumeRandomLengthString();
+ db->Delete(ROCKSDB_NAMESPACE::WriteOptions(), key);
+ break;
+ }
+ case kGetProperty: {
+ std::string prop;
+ std::string property_name = fuzzed_data.ConsumeRandomLengthString();
+ db->GetProperty(property_name, &prop);
+ break;
+ }
+ case kIterator: {
+ ROCKSDB_NAMESPACE::Iterator* it =
+ db->NewIterator(ROCKSDB_NAMESPACE::ReadOptions());
+ for (it->SeekToFirst(); it->Valid(); it->Next()) {
+ }
+ delete it;
+ break;
+ }
+ case kSnapshot: {
+ ROCKSDB_NAMESPACE::ReadOptions snapshot_options;
+ snapshot_options.snapshot = db->GetSnapshot();
+ ROCKSDB_NAMESPACE::Iterator* it = db->NewIterator(snapshot_options);
+ db->ReleaseSnapshot(snapshot_options.snapshot);
+ delete it;
+ break;
+ }
+ case kOpenClose: {
+ db->Close();
+ delete db;
+ status = ROCKSDB_NAMESPACE::DB::Open(options, db_path, &db);
+ if (!status.ok()) {
+ ROCKSDB_NAMESPACE::DestroyDB(db_path, options);
+ return 0;
+ }
+
+ break;
+ }
+ case kColumn: {
+ ROCKSDB_NAMESPACE::ColumnFamilyHandle* cf;
+ ROCKSDB_NAMESPACE::Status s;
+ s = db->CreateColumnFamily(ROCKSDB_NAMESPACE::ColumnFamilyOptions(),
+ "new_cf", &cf);
+ s = db->DestroyColumnFamilyHandle(cf);
+ db->Close();
+ delete db;
+
+ // open DB with two column families
+ std::vector<ROCKSDB_NAMESPACE::ColumnFamilyDescriptor> column_families;
+ // have to open default column family
+ column_families.push_back(ROCKSDB_NAMESPACE::ColumnFamilyDescriptor(
+ ROCKSDB_NAMESPACE::kDefaultColumnFamilyName,
+ ROCKSDB_NAMESPACE::ColumnFamilyOptions()));
+ // open the new one, too
+ column_families.push_back(ROCKSDB_NAMESPACE::ColumnFamilyDescriptor(
+ "new_cf", ROCKSDB_NAMESPACE::ColumnFamilyOptions()));
+ std::vector<ROCKSDB_NAMESPACE::ColumnFamilyHandle*> handles;
+ s = ROCKSDB_NAMESPACE::DB::Open(ROCKSDB_NAMESPACE::DBOptions(), db_path,
+ column_families, &handles, &db);
+
+ if (s.ok()) {
+ std::string key1 = fuzzed_data.ConsumeRandomLengthString();
+ std::string val1 = fuzzed_data.ConsumeRandomLengthString();
+ std::string key2 = fuzzed_data.ConsumeRandomLengthString();
+ s = db->Put(ROCKSDB_NAMESPACE::WriteOptions(), handles[1], key1,
+ val1);
+ std::string value;
+ s = db->Get(ROCKSDB_NAMESPACE::ReadOptions(), handles[1], key2,
+ &value);
+ s = db->DropColumnFamily(handles[1]);
+ for (auto handle : handles) {
+ s = db->DestroyColumnFamilyHandle(handle);
+ }
+ } else {
+ status = ROCKSDB_NAMESPACE::DB::Open(options, db_path, &db);
+ if (!status.ok()) {
+ // At this point there is no saving to do. So we exit
+ ROCKSDB_NAMESPACE::DestroyDB(db_path, ROCKSDB_NAMESPACE::Options());
+ return 0;
+ }
+ }
+ break;
+ }
+ case kCompactRange: {
+ std::string slice_start = fuzzed_data.ConsumeRandomLengthString();
+ std::string slice_end = fuzzed_data.ConsumeRandomLengthString();
+
+ ROCKSDB_NAMESPACE::Slice begin(slice_start);
+ ROCKSDB_NAMESPACE::Slice end(slice_end);
+ ROCKSDB_NAMESPACE::CompactRangeOptions options;
+ ROCKSDB_NAMESPACE::Status s = db->CompactRange(options, &begin, &end);
+ break;
+ }
+ case kSeekForPrev: {
+ std::string key = fuzzed_data.ConsumeRandomLengthString();
+ auto iter = db->NewIterator(ROCKSDB_NAMESPACE::ReadOptions());
+ iter->SeekForPrev(key);
+ delete iter;
+ break;
+ }
+ case OP_COUNT:
+ break;
+ }
+ }
+
+ // Cleanup DB
+ db->Close();
+ delete db;
+ ROCKSDB_NAMESPACE::DestroyDB(db_path, options);
+ return 0;
+}