Line | Count | Source (jump to first uncovered line) |
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 | | |
42 | 5.46k | ~AutoDbDeleter() { |
43 | 5.46k | std::filesystem::remove_all(kDbPath); |
44 | 5.46k | } |
45 | | }; |
46 | | |
47 | | // static |
48 | | constexpr char AutoDbDeleter::kDbPath[]; |
49 | | |
50 | | // Returns nullptr (a falsey unique_ptr) if opening fails. |
51 | 123k | std::unique_ptr<leveldb::DB> OpenDB() { |
52 | 123k | leveldb::Options options; |
53 | 123k | options.create_if_missing = true; |
54 | | |
55 | 123k | leveldb::DB* db_ptr; |
56 | 123k | leveldb::Status status = |
57 | 123k | leveldb::DB::Open(options, AutoDbDeleter::kDbPath, &db_ptr); |
58 | 123k | if (!status.ok()) |
59 | 0 | return nullptr; |
60 | | |
61 | 123k | return std::unique_ptr<leveldb::DB>(db_ptr); |
62 | 123k | } |
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 | | |
81 | 5.46k | 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 | 5.46k | AutoDbDeleter db_deleter; |
84 | | |
85 | 5.46k | std::unique_ptr<leveldb::DB> db = OpenDB(); |
86 | 5.46k | if (!db.get()) |
87 | 0 | return 0; |
88 | | |
89 | | // Perform a sequence of operations on the database. |
90 | 5.46k | FuzzedDataProvider fuzzed_data(data, size); |
91 | 2.55M | while (fuzzed_data.remaining_bytes() != 0) { |
92 | 2.54M | FuzzOp fuzz_op = fuzzed_data.ConsumeEnum<FuzzOp>(); |
93 | | |
94 | 2.54M | switch (fuzz_op) { |
95 | 52.1k | case FuzzOp::kPut: { |
96 | 52.1k | std::string key = fuzzed_data.ConsumeRandomLengthString(); |
97 | 52.1k | std::string value = fuzzed_data.ConsumeRandomLengthString(); |
98 | 52.1k | db->Put(leveldb::WriteOptions(), key, value); |
99 | 52.1k | break; |
100 | 0 | } |
101 | 49.8k | case FuzzOp::kGet: { |
102 | 49.8k | std::string key = fuzzed_data.ConsumeRandomLengthString(); |
103 | 49.8k | std::string value; |
104 | 49.8k | db->Get(leveldb::ReadOptions(), key, &value); |
105 | 49.8k | break; |
106 | 0 | } |
107 | 2.28M | case FuzzOp::kDelete: { |
108 | 2.28M | std::string key = fuzzed_data.ConsumeRandomLengthString(); |
109 | 2.28M | db->Delete(leveldb::WriteOptions(), key); |
110 | 2.28M | break; |
111 | 0 | } |
112 | 11.0k | case FuzzOp::kGetProperty: { |
113 | 11.0k | std::string name = fuzzed_data.ConsumeRandomLengthString(); |
114 | 11.0k | std::string value; |
115 | 11.0k | db->GetProperty(name, &value); |
116 | 11.0k | break; |
117 | 0 | } |
118 | 68.9k | case FuzzOp::kIterate: { |
119 | 68.9k | std::unique_ptr<leveldb::Iterator> it( |
120 | 68.9k | db->NewIterator(leveldb::ReadOptions())); |
121 | 457k | for (it->SeekToFirst(); it->Valid(); it->Next()) |
122 | 388k | continue; |
123 | 68.9k | } |
124 | 87.8k | case FuzzOp::kGetReleaseSnapshot: { |
125 | 87.8k | leveldb::ReadOptions snapshot_options; |
126 | 87.8k | snapshot_options.snapshot = db->GetSnapshot(); |
127 | 87.8k | std::unique_ptr<leveldb::Iterator> it(db->NewIterator(snapshot_options)); |
128 | 87.8k | db->ReleaseSnapshot(snapshot_options.snapshot); |
129 | 87.8k | } |
130 | 118k | 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 | 118k | db.reset(); |
134 | 118k | db = OpenDB(); |
135 | 118k | if (!db) |
136 | 0 | return 0; // Reopening the database failed. |
137 | 118k | break; |
138 | 118k | } |
139 | 118k | case FuzzOp::kCompactRange: { |
140 | 34.3k | std::string begin_key = fuzzed_data.ConsumeRandomLengthString(); |
141 | 34.3k | std::string end_key = fuzzed_data.ConsumeRandomLengthString(); |
142 | 34.3k | leveldb::Slice begin_slice(begin_key); |
143 | 34.3k | leveldb::Slice end_slice(end_key); |
144 | 34.3k | db->CompactRange(&begin_slice, &end_slice); |
145 | 34.3k | break; |
146 | 118k | } |
147 | 2.54M | } |
148 | 2.54M | } |
149 | | |
150 | 5.46k | return 0; |
151 | 5.46k | } |