1 /* Copyright 2020 Google Inc.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7       http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 */
15 
16 
17 #include <cstdint>
18 #include <cstddef>
19 #include <filesystem>
20 #include <memory>
21 #include <string>
22 
23 #include "leveldb/db.h"
24 #include "leveldb/iterator.h"
25 #include "leveldb/options.h"
26 #include "leveldb/status.h"
27 
28 #include <fuzzer/FuzzedDataProvider.h>
29 
30 namespace {
31 
32 // Deletes the database directory when going out of scope.
33 class AutoDbDeleter {
34  public:
35   static constexpr char kDbPath[] = "/tmp/testdb";
36 
37   AutoDbDeleter() = default;
38 
39   AutoDbDeleter(const AutoDbDeleter&) = delete;
40   AutoDbDeleter& operator=(const AutoDbDeleter&) = delete;
41 
~AutoDbDeleter()42   ~AutoDbDeleter() {
43     std::__fs::filesystem::remove_all(kDbPath);
44   }
45 };
46 
47 // static
48 constexpr char AutoDbDeleter::kDbPath[];
49 
50 // Returns nullptr (a falsey unique_ptr) if opening fails.
OpenDB()51 std::unique_ptr<leveldb::DB> OpenDB() {
52   leveldb::Options options;
53   options.create_if_missing = true;
54 
55   leveldb::DB* db_ptr;
56   leveldb::Status status =
57       leveldb::DB::Open(options, AutoDbDeleter::kDbPath, &db_ptr);
58   if (!status.ok())
59     return nullptr;
60 
61   return std::unique_ptr<leveldb::DB>(db_ptr);
62 }
63 
64 enum class FuzzOp {
65   kPut = 0,
66   kGet = 1,
67   kDelete = 2,
68   kGetProperty = 3,
69   kIterate = 4,
70   kGetReleaseSnapshot = 5,
71   kReopenDb = 6,
72   kCompactRange = 7,
73   // Add new values here.
74 
75   // When adding new values, update to the last value above.
76   kMaxValue = kCompactRange,
77 };
78 
79 }  // namespace
80 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)81 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
82   // Must occur before `db` so the deletion doesn't happen while the DB is open.
83   AutoDbDeleter db_deleter;
84 
85   std::unique_ptr<leveldb::DB> db = OpenDB();
86   if (!db.get())
87     return 0;
88 
89   // Perform a sequence of operations on the database.
90   FuzzedDataProvider fuzzed_data(data, size);
91   while (fuzzed_data.remaining_bytes() != 0) {
92     FuzzOp fuzz_op = fuzzed_data.ConsumeEnum<FuzzOp>();
93 
94     switch (fuzz_op) {
95     case FuzzOp::kPut: {
96       std::string key = fuzzed_data.ConsumeRandomLengthString();
97       std::string value = fuzzed_data.ConsumeRandomLengthString();
98       db->Put(leveldb::WriteOptions(), key, value);
99       break;
100     }
101     case FuzzOp::kGet: {
102       std::string key = fuzzed_data.ConsumeRandomLengthString();
103       std::string value;
104       db->Get(leveldb::ReadOptions(), key, &value);
105       break;
106     }
107     case FuzzOp::kDelete: {
108       std::string key = fuzzed_data.ConsumeRandomLengthString();
109       db->Delete(leveldb::WriteOptions(), key);
110       break;
111     }
112     case FuzzOp::kGetProperty: {
113       std::string name = fuzzed_data.ConsumeRandomLengthString();
114       std::string value;
115       db->GetProperty(name, &value);
116       break;
117     }
118     case FuzzOp::kIterate: {
119       std::unique_ptr<leveldb::Iterator> it(
120           db->NewIterator(leveldb::ReadOptions()));
121       for (it->SeekToFirst(); it->Valid(); it->Next())
122         continue;
123     }
124     case FuzzOp::kGetReleaseSnapshot: {
125       leveldb::ReadOptions snapshot_options;
126       snapshot_options.snapshot = db->GetSnapshot();
127       std::unique_ptr<leveldb::Iterator> it(db->NewIterator(snapshot_options));
128       db->ReleaseSnapshot(snapshot_options.snapshot);
129     }
130     case FuzzOp::kReopenDb: {
131       // The database must be closed before attempting to reopen it. Otherwise,
132       // the open will fail due to exclusive locking.
133       db.reset();
134       db = OpenDB();
135       if (!db)
136         return 0;  // Reopening the database failed.
137       break;
138     }
139     case FuzzOp::kCompactRange: {
140       std::string begin_key = fuzzed_data.ConsumeRandomLengthString();
141       std::string end_key =  fuzzed_data.ConsumeRandomLengthString();
142       leveldb::Slice begin_slice(begin_key);
143       leveldb::Slice end_slice(end_key);
144       db->CompactRange(&begin_slice, &end_slice);
145       break;
146     }
147     }
148   }
149 
150   return 0;
151 }
152