You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

152 lines
4.2 KiB

/* Copyright 2020 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <cstdint>
#include <cstddef>
#include <filesystem>
#include <memory>
#include <string>
#include "leveldb/db.h"
#include "leveldb/iterator.h"
#include "leveldb/options.h"
#include "leveldb/status.h"
#include <fuzzer/FuzzedDataProvider.h>
namespace {
// Deletes the database directory when going out of scope.
class AutoDbDeleter {
public:
static constexpr char kDbPath[] = "/tmp/testdb";
AutoDbDeleter() = default;
AutoDbDeleter(const AutoDbDeleter&) = delete;
AutoDbDeleter& operator=(const AutoDbDeleter&) = delete;
~AutoDbDeleter() {
std::__fs::filesystem::remove_all(kDbPath);
}
};
// static
constexpr char AutoDbDeleter::kDbPath[];
// Returns nullptr (a falsey unique_ptr) if opening fails.
std::unique_ptr<leveldb::DB> OpenDB() {
leveldb::Options options;
options.create_if_missing = true;
leveldb::DB* db_ptr;
leveldb::Status status =
leveldb::DB::Open(options, AutoDbDeleter::kDbPath, &db_ptr);
if (!status.ok())
return nullptr;
return std::unique_ptr<leveldb::DB>(db_ptr);
}
enum class FuzzOp {
kPut = 0,
kGet = 1,
kDelete = 2,
kGetProperty = 3,
kIterate = 4,
kGetReleaseSnapshot = 5,
kReopenDb = 6,
kCompactRange = 7,
// Add new values here.
// When adding new values, update to the last value above.
kMaxValue = kCompactRange,
};
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// Must occur before `db` so the deletion doesn't happen while the DB is open.
AutoDbDeleter db_deleter;
std::unique_ptr<leveldb::DB> db = OpenDB();
if (!db.get())
return 0;
// Perform a sequence of operations on the database.
FuzzedDataProvider fuzzed_data(data, size);
while (fuzzed_data.remaining_bytes() != 0) {
FuzzOp fuzz_op = fuzzed_data.ConsumeEnum<FuzzOp>();
switch (fuzz_op) {
case FuzzOp::kPut: {
std::string key = fuzzed_data.ConsumeRandomLengthString();
std::string value = fuzzed_data.ConsumeRandomLengthString();
db->Put(leveldb::WriteOptions(), key, value);
break;
}
case FuzzOp::kGet: {
std::string key = fuzzed_data.ConsumeRandomLengthString();
std::string value;
db->Get(leveldb::ReadOptions(), key, &value);
break;
}
case FuzzOp::kDelete: {
std::string key = fuzzed_data.ConsumeRandomLengthString();
db->Delete(leveldb::WriteOptions(), key);
break;
}
case FuzzOp::kGetProperty: {
std::string name = fuzzed_data.ConsumeRandomLengthString();
std::string value;
db->GetProperty(name, &value);
break;
}
case FuzzOp::kIterate: {
std::unique_ptr<leveldb::Iterator> it(
db->NewIterator(leveldb::ReadOptions()));
for (it->SeekToFirst(); it->Valid(); it->Next())
continue;
}
case FuzzOp::kGetReleaseSnapshot: {
leveldb::ReadOptions snapshot_options;
snapshot_options.snapshot = db->GetSnapshot();
std::unique_ptr<leveldb::Iterator> it(db->NewIterator(snapshot_options));
db->ReleaseSnapshot(snapshot_options.snapshot);
}
case FuzzOp::kReopenDb: {
// The database must be closed before attempting to reopen it. Otherwise,
// the open will fail due to exclusive locking.
db.reset();
db = OpenDB();
if (!db)
return 0; // Reopening the database failed.
break;
}
case FuzzOp::kCompactRange: {
std::string begin_key = fuzzed_data.ConsumeRandomLengthString();
std::string end_key = fuzzed_data.ConsumeRandomLengthString();
leveldb::Slice begin_slice(begin_key);
leveldb::Slice end_slice(end_key);
db->CompactRange(&begin_slice, &end_slice);
break;
}
}
}
return 0;
}