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