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
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;
|
|
}
|