// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <fuzzer/FuzzedDataProvider.h>
#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#include "third_party/leveldatabase/src/include/leveldb/env.h"
#include "third_party/leveldatabase/src/include/leveldb/options.h"
#include "third_party/leveldatabase/src/include/leveldb/slice.h"
#define FUZZING_ASSERT(condition) \
if (!(condition)) { \
fprintf(stderr, "%s\n", "Fuzzing Assertion Failure: " #condition); \
abort(); \
}
using leveldb::DB;
using leveldb::Env;
using leveldb::Options;
using leveldb::ReadOptions;
using leveldb::Slice;
using leveldb::Status;
using leveldb::WriteOptions;
// We need to use keys and values both shorter and longer than 128 bytes in
// order to cover both fast and slow paths in DecodeEntry.
static constexpr size_t kMaxKeyLen = 256;
static constexpr size_t kMaxValueLen = 256;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Reject too long inputs as they may cause non actionable timeouts issues.
if (size > 128 * 1024)
return 0;
Env* mem_env = NewMemEnv(Env::Default());
FuzzedDataProvider data_provider(data, size);
Options open_opts;
open_opts.create_if_missing = true;
open_opts.paranoid_checks = data_provider.ConsumeBool();
open_opts.reuse_logs = false;
open_opts.env = mem_env;
ReadOptions read_opts;
read_opts.verify_checksums = data_provider.ConsumeBool();
read_opts.fill_cache = data_provider.ConsumeBool();
WriteOptions write_opts;
write_opts.sync = data_provider.ConsumeBool();
DB* db = nullptr;
Status open_status = DB::Open(open_opts, "leveldbfuzztest", &db);
FUZZING_ASSERT(open_status.ok());
// Put a couple constant values which must be successfully written.
FUZZING_ASSERT(db->Put(write_opts, "key1", "val1").ok());
FUZZING_ASSERT(db->Put(write_opts, "key2", "val2").ok());
// Split the data into a sequence of (key, value) strings and put those in.
// Also collect both keys and values to be used as keys for retrieval below.
std::vector<std::string> strings_used;
while (data_provider.remaining_bytes()) {
std::string key = data_provider.ConsumeRandomLengthString(kMaxKeyLen);
std::string value = data_provider.ConsumeRandomLengthString(kMaxValueLen);
db->Put(write_opts, key, value);
strings_used.push_back(key);
strings_used.push_back(value);
}
// Use all the strings we have extracted from the data previously as the keys.
for (const auto& key : strings_used) {
std::string db_value;
db->Get(read_opts, Slice(key.data(), key.size()), &db_value);
}
// Delete all keys previously written to the database.
for (const auto& key : strings_used) {
db->Delete(write_opts, Slice(key.data(), key.size()));
}
delete db;
db = nullptr;
delete mem_env;
mem_env = nullptr;
return 0;
}