1 #include <fuzzer/FuzzedDataProvider.h>
2
3 #include "rocksdb/db.h"
4
5 enum OperationType {
6 kPut,
7 kGet,
8 kDelete,
9 kGetProperty,
10 kIterator,
11 kSnapshot,
12 kOpenClose,
13 kColumn,
14 kCompactRange,
15 kSeekForPrev,
16 OP_COUNT
17 };
18
19 constexpr char db_path[] = "/tmp/testdb";
20
21 // Fuzzes DB operations by doing interpretations on the data. Both the
22 // sequence of API calls to be called on the DB as well as the arguments
23 // to each of these APIs are interpreted by way of the data buffer.
24 // The operations that the fuzzer supports are given by the OperationType
25 // enum. The goal is to capture sanitizer bugs, so the code should be
26 // compiled with a given sanitizer (ASan, UBSan, MSan).
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)27 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
28 rocksdb::DB* db;
29 rocksdb::Options options;
30 options.create_if_missing = true;
31 rocksdb::Status status = rocksdb::DB::Open(options, db_path, &db);
32 if (!status.ok()) {
33 return 0;
34 }
35 FuzzedDataProvider fuzzed_data(data, size);
36
37 // perform a sequence of calls on our db instance
38 int max_iter = static_cast<int>(data[0]);
39 for (int i = 0; i < max_iter && i < size; i++) {
40 OperationType op = static_cast<OperationType>(data[i] % OP_COUNT);
41
42 switch (op) {
43 case kPut: {
44 std::string key = fuzzed_data.ConsumeRandomLengthString();
45 std::string val = fuzzed_data.ConsumeRandomLengthString();
46 db->Put(rocksdb::WriteOptions(), key, val);
47 break;
48 }
49 case kGet: {
50 std::string key = fuzzed_data.ConsumeRandomLengthString();
51 std::string value;
52 db->Get(rocksdb::ReadOptions(), key, &value);
53 break;
54 }
55 case kDelete: {
56 std::string key = fuzzed_data.ConsumeRandomLengthString();
57 db->Delete(rocksdb::WriteOptions(), key);
58 break;
59 }
60 case kGetProperty: {
61 std::string prop;
62 std::string property_name = fuzzed_data.ConsumeRandomLengthString();
63 db->GetProperty(property_name, &prop);
64 break;
65 }
66 case kIterator: {
67 rocksdb::Iterator* it = db->NewIterator(rocksdb::ReadOptions());
68 for (it->SeekToFirst(); it->Valid(); it->Next()) {
69 }
70 delete it;
71 break;
72 }
73 case kSnapshot: {
74 rocksdb::ReadOptions snapshot_options;
75 snapshot_options.snapshot = db->GetSnapshot();
76 rocksdb::Iterator* it = db->NewIterator(snapshot_options);
77 db->ReleaseSnapshot(snapshot_options.snapshot);
78 delete it;
79 break;
80 }
81 case kOpenClose: {
82 db->Close();
83 delete db;
84 status = rocksdb::DB::Open(options, db_path, &db);
85 if (!status.ok()) {
86 rocksdb::DestroyDB(db_path, options);
87 return 0;
88 }
89
90 break;
91 }
92 case kColumn: {
93 rocksdb::ColumnFamilyHandle* cf;
94 rocksdb::Status s;
95 s = db->CreateColumnFamily(rocksdb::ColumnFamilyOptions(), "new_cf",
96 &cf);
97 s = db->DestroyColumnFamilyHandle(cf);
98 db->Close();
99 delete db;
100
101 // open DB with two column families
102 std::vector<rocksdb::ColumnFamilyDescriptor> column_families;
103 // have to open default column family
104 column_families.push_back(rocksdb::ColumnFamilyDescriptor(
105 rocksdb::kDefaultColumnFamilyName, rocksdb::ColumnFamilyOptions()));
106 // open the new one, too
107 column_families.push_back(rocksdb::ColumnFamilyDescriptor(
108 "new_cf", rocksdb::ColumnFamilyOptions()));
109 std::vector<rocksdb::ColumnFamilyHandle*> handles;
110 s = rocksdb::DB::Open(rocksdb::DBOptions(), db_path, column_families,
111 &handles, &db);
112
113 if (s.ok()) {
114 std::string key1 = fuzzed_data.ConsumeRandomLengthString();
115 std::string val1 = fuzzed_data.ConsumeRandomLengthString();
116 std::string key2 = fuzzed_data.ConsumeRandomLengthString();
117 s = db->Put(rocksdb::WriteOptions(), handles[1], key1, val1);
118 std::string value;
119 s = db->Get(rocksdb::ReadOptions(), handles[1], key2, &value);
120 s = db->DropColumnFamily(handles[1]);
121 for (auto handle : handles) {
122 s = db->DestroyColumnFamilyHandle(handle);
123 }
124 } else {
125 status = rocksdb::DB::Open(options, db_path, &db);
126 if (!status.ok()) {
127 // At this point there is no saving to do. So we exit
128 rocksdb::DestroyDB(db_path, rocksdb::Options());
129 return 0;
130 }
131 }
132 break;
133 }
134 case kCompactRange: {
135 std::string slice_start = fuzzed_data.ConsumeRandomLengthString();
136 std::string slice_end = fuzzed_data.ConsumeRandomLengthString();
137
138 rocksdb::Slice begin(slice_start);
139 rocksdb::Slice end(slice_end);
140 rocksdb::CompactRangeOptions options;
141 rocksdb::Status s = db->CompactRange(options, &begin, &end);
142 break;
143 }
144 case kSeekForPrev: {
145 std::string key = fuzzed_data.ConsumeRandomLengthString();
146 auto iter = db->NewIterator(rocksdb::ReadOptions());
147 iter->SeekForPrev(key);
148 delete iter;
149 break;
150 }
151 }
152 }
153
154 // Cleanup DB
155 db->Close();
156 delete db;
157 rocksdb::DestroyDB(db_path, options);
158 return 0;
159 }
160