152 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			152 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* 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;
 | |
| }
 |