aboutsummaryrefslogtreecommitdiff
path: root/projects/leveldb
diff options
context:
space:
mode:
authorVictor Costan <costan@google.com>2020-12-13 19:46:37 -0800
committerGitHub <noreply@github.com>2020-12-14 14:46:37 +1100
commit6af0e55ecd6b8fb735fe539bbd4a829f2e38dca4 (patch)
treeb59fde02bfc6fad320554d553abc08b700ded364 /projects/leveldb
parent0f3d14845e128e3be0e61b75648d5d44532f8a44 (diff)
downloadoss-fuzz-6af0e55ecd6b8fb735fe539bbd4a829f2e38dca4.tar.gz
leveldb: Fuzzer improvements. (#4837)
* Stop when opening a database fails. This will avoid null pointer dereferences. * Use C++11 smart pointers for leveldb::DB and leveldb::Iterator. This makes it easier to ensure the fuzzer doesn't leak memory. No leak was detected while applying this fix. * Use the FuzzedDataProvider API exclusively for consuming data. This makes it easier to ensure maximum fuzzer coverage. * Avoid building unnecessary code (tests, benchmarks). This slightly reduces oss-fuzz resource usage. * Use an enum class and FuzzedDataProvider::ConsumeEnum() instead of reimplementing it. This makes it easier to extend the fuzzer with new operations in the future. * Use meaningful names (key, value, name) instead of tmp* for local variables storing leveldb API inputs.
Diffstat (limited to 'projects/leveldb')
-rwxr-xr-xprojects/leveldb/build.sh7
-rw-r--r--projects/leveldb/fuzz_db.cc173
2 files changed, 118 insertions, 62 deletions
diff --git a/projects/leveldb/build.sh b/projects/leveldb/build.sh
index 2375288fe..7668c045a 100755
--- a/projects/leveldb/build.sh
+++ b/projects/leveldb/build.sh
@@ -18,13 +18,14 @@
cd $SRC/leveldb
mkdir -p build && cd build
-cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .
+cmake -DCMAKE_BUILD_TYPE=Release -DLEVELDB_BUILD_TESTS=0 \
+ -DLEVELDB_BUILD_BENCHMARKS=0 .. && cmake --build .
for fuzzer in fuzz_db; do
# Compile
$CXX $CXXFLAGS -c ../${fuzzer}.cc -o ${fuzzer}.o \
- -DLEVELDB_PLATFORM_POSIX=1 -std=c++11 \
- -I$SRC/leveldb/build/include -I$SRC/leveldb/ -I$SRC/leveldb/include
+ -DLEVELDB_PLATFORM_POSIX=1 -std=c++11 -Wall \
+ -I$SRC/leveldb/build/include -I$SRC/leveldb/ -I$SRC/leveldb/include
# Link
$CXX $LIB_FUZZING_ENGINE $CXXFLAGS ${fuzzer}.o -o $OUT/${fuzzer} libleveldb.a
diff --git a/projects/leveldb/fuzz_db.cc b/projects/leveldb/fuzz_db.cc
index a9dfb4823..0147c124f 100644
--- a/projects/leveldb/fuzz_db.cc
+++ b/projects/leveldb/fuzz_db.cc
@@ -13,84 +13,139 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-#include "leveldb/db.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string>
-#include <string.h>
+
+#include <cstdint>
+#include <cstddef>
#include <filesystem>
+#include <memory>
+#include <string>
+
+#include "leveldb/db.h"
+#include "leveldb/iterator.h"
+#include "leveldb/options.h"
+#include "leveldb/status.h"
#include <fuzzer/FuzzedDataProvider.h>
+namespace {
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
- // We need at least one byte
- if (size == 0) {
- return 0;
+// Deletes the database directory when going out of scope.
+class AutoDbDeleter {
+ public:
+ static constexpr char kDbPath[] = "/tmp/testdb";
+
+ AutoDbDeleter() = default;
+
+ AutoDbDeleter(const AutoDbDeleter&) = delete;
+ AutoDbDeleter& operator=(const AutoDbDeleter&) = delete;
+
+ ~AutoDbDeleter() {
+ std::__fs::filesystem::remove_all(kDbPath);
}
+};
- FuzzedDataProvider fuzzed_data(data, size);
+// static
+constexpr char AutoDbDeleter::kDbPath[];
- leveldb::DB* db;
+// Returns nullptr (a falsey unique_ptr) if opening fails.
+std::unique_ptr<leveldb::DB> OpenDB() {
leveldb::Options options;
options.create_if_missing = true;
- leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
-
- std::string value;
-
- // perform a sequence of calls on our db instance
- int max_iter = (int)data[0];
- for(int i=0; i < max_iter && i < size; i++) {
- #define SIZE_OF_FUNCS 8
- size_t c = fuzzed_data.ConsumeIntegral<uint8_t>() % SIZE_OF_FUNCS;
-
- if(c == 0) { // PUT
- std::string tmp1 = fuzzed_data.ConsumeRandomLengthString();
- std::string tmp2 = fuzzed_data.ConsumeRandomLengthString();
- db->Put(leveldb::WriteOptions(), tmp1, tmp2);
- }
- else if(c == 1) { // Get
- std::string tmp3 = fuzzed_data.ConsumeRandomLengthString();
- db->Get(leveldb::ReadOptions(), tmp3, &value);
- }
- else if (c == 2) { // Delete
- std::string tmp4 = fuzzed_data.ConsumeRandomLengthString();
- db->Delete(leveldb::WriteOptions(), tmp4);
+
+ leveldb::DB* db_ptr;
+ leveldb::Status status =
+ leveldb::DB::Open(options, AutoDbDeleter::kDbPath, &db_ptr);
+ if (!status.ok())
+ return nullptr;
+
+ return std::unique_ptr<leveldb::DB>(db_ptr);
+}
+
+enum class FuzzOp {
+ kPut = 0,
+ kGet = 1,
+ kDelete = 2,
+ kGetProperty = 3,
+ kIterate = 4,
+ kGetReleaseSnapshot = 5,
+ kReopenDb = 6,
+ kCompactRange = 7,
+ // Add new values here.
+
+ // When adding new values, update to the last value above.
+ kMaxValue = kCompactRange,
+};
+
+} // namespace
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ // Must occur before `db` so the deletion doesn't happen while the DB is open.
+ AutoDbDeleter db_deleter;
+
+ std::unique_ptr<leveldb::DB> db = OpenDB();
+ if (!db.get())
+ return 0;
+
+ // Perform a sequence of operations on the database.
+ FuzzedDataProvider fuzzed_data(data, size);
+ while (fuzzed_data.remaining_bytes() != 0) {
+ FuzzOp fuzz_op = fuzzed_data.ConsumeEnum<FuzzOp>();
+
+ switch (fuzz_op) {
+ case FuzzOp::kPut: {
+ std::string key = fuzzed_data.ConsumeRandomLengthString();
+ std::string value = fuzzed_data.ConsumeRandomLengthString();
+ db->Put(leveldb::WriteOptions(), key, value);
+ break;
+ }
+ case FuzzOp::kGet: {
+ std::string key = fuzzed_data.ConsumeRandomLengthString();
+ std::string value;
+ db->Get(leveldb::ReadOptions(), key, &value);
+ break;
+ }
+ case FuzzOp::kDelete: {
+ std::string key = fuzzed_data.ConsumeRandomLengthString();
+ db->Delete(leveldb::WriteOptions(), key);
+ break;
}
- else if (c == 3) { // GetProperty
- std::string prop;
- std::string tmp = fuzzed_data.ConsumeRandomLengthString();
- db->GetProperty(tmp, &prop);
+ case FuzzOp::kGetProperty: {
+ std::string name = fuzzed_data.ConsumeRandomLengthString();
+ std::string value;
+ db->GetProperty(name, &value);
+ break;
}
- else if(c == 4) { // Iterator
- leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
- for (it->SeekToFirst(); it->Valid(); it->Next()) {
+ case FuzzOp::kIterate: {
+ std::unique_ptr<leveldb::Iterator> it(
+ db->NewIterator(leveldb::ReadOptions()));
+ for (it->SeekToFirst(); it->Valid(); it->Next())
continue;
- }
- delete it;
- }
- else if(c == 5) { // GetSnapshot and Release Snapshot
+ }
+ case FuzzOp::kGetReleaseSnapshot: {
leveldb::ReadOptions snapshot_options;
snapshot_options.snapshot = db->GetSnapshot();
- leveldb::Iterator* it = db->NewIterator(snapshot_options);
+ std::unique_ptr<leveldb::Iterator> it(db->NewIterator(snapshot_options));
db->ReleaseSnapshot(snapshot_options.snapshot);
- delete it;
- }
- else if(c == 6) { // Open and close DB
- delete db;
- status = leveldb::DB::Open(options, "/tmp/testdb", &db);
}
- else if (c == 7) {
- std::string tmp1 = fuzzed_data.ConsumeRandomLengthString();
- std::string tmp2 = fuzzed_data.ConsumeRandomLengthString();
- leveldb::Slice s1 =tmp1;
- leveldb::Slice s2 = tmp2;
- db->CompactRange(&s1, &s2);
+ case FuzzOp::kReopenDb: {
+ // The database must be closed before attempting to reopen it. Otherwise,
+ // the open will fail due to exclusive locking.
+ db.reset();
+ db = OpenDB();
+ if (!db)
+ return 0; // Reopening the database failed.
+ break;
+ }
+ case FuzzOp::kCompactRange: {
+ std::string begin_key = fuzzed_data.ConsumeRandomLengthString();
+ std::string end_key = fuzzed_data.ConsumeRandomLengthString();
+ leveldb::Slice begin_slice(begin_key);
+ leveldb::Slice end_slice(end_key);
+ db->CompactRange(&begin_slice, &end_slice);
+ break;
+ }
}
}
- // Cleanup DB
- delete db;
- std::__fs::filesystem::remove_all("/tmp/testdb");
return 0;
}