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