• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

proto/H14-Dec-2021-2923

MakefileH A D14-Dec-20212.6 KiB6241

README.mdH A D14-Dec-20217.2 KiB161122

db_fuzzer.ccH A D14-Dec-20215.2 KiB160136

db_map_fuzzer.ccH A D14-Dec-20213.3 KiB10485

sst_file_writer_fuzzer.ccH A D14-Dec-20216.2 KiB188155

util.hH A D14-Dec-20211 KiB2420

README.md

1# Fuzzing RocksDB
2
3## Overview
4
5This directory contains [fuzz tests](https://en.wikipedia.org/wiki/Fuzzing) for RocksDB.
6RocksDB testing infrastructure currently includes unit tests and [stress tests](https://github.com/facebook/rocksdb/wiki/Stress-test),
7we hope fuzz testing can catch more bugs.
8
9## Prerequisite
10
11We use [LLVM libFuzzer](http://llvm.org/docs/LibFuzzer.html) as the fuzzying engine,
12so make sure you have [clang](https://clang.llvm.org/get_started.html) as your compiler.
13
14Some tests rely on [structure aware fuzzing](https://github.com/google/fuzzing/blob/master/docs/structure-aware-fuzzing.md).
15We use [protobuf](https://developers.google.com/protocol-buffers) to define structured input to the fuzzer,
16and use [libprotobuf-mutator](https://github.com/google/libprotobuf-mutator) as the custom libFuzzer mutator.
17So make sure you have protobuf and libprotobuf-mutator installed, and make sure `pkg-config` can find them.
18
19## Example
20
21This example shows you how to do structure aware fuzzing to `rocksdb::SstFileWriter`.
22
23After walking through the steps to create the fuzzer, we'll introduce a bug into `rocksdb::SstFileWriter::Put`,
24then show that the fuzzer can catch the bug.
25
26### Design the test
27
28We want the fuzzing engine to automatically generate a list of database operations,
29then we apply these operations to `SstFileWriter` in sequence,
30finally, after the SST file is generated, we use `SstFileReader` to check the file's checksum.
31
32### Define input
33
34We define the database operations in protobuf, each operation has a type of operation and a key value pair,
35see [proto/db_operation.proto](proto/db_operation.proto) for details.
36
37### Define tests with the input
38
39In [sst_file_writer_fuzzer.cc](sst_file_writer_fuzzer.cc),
40we define the tests to be run on the generated input:
41
42```
43DEFINE_PROTO_FUZZER(DBOperations& input) {
44  // apply the operations to SstFileWriter and use SstFileReader to verify checksum.
45  // ...
46}
47```
48
49`SstFileWriter` requires the keys of the operations to be unique and be in ascending order,
50but the fuzzing engine generates the input randomly, so we need to process the generated input before
51passing it to `DEFINE_PROTO_FUZZER`, this is accomplished by registering a post processor:
52
53```
54protobuf_mutator::libfuzzer::PostProcessorRegistration<DBOperations>
55```
56
57### Compile and link the fuzzer
58
59In the rocksdb root directory, compile rocksdb library by `make static_lib`.
60
61Go to the `fuzz` directory,
62run `make sst_file_writer_fuzzer` to generate the fuzzer,
63it will compile rocksdb static library, generate protobuf, then compile and link `sst_file_writer_fuzzer`.
64
65### Introduce a bug
66
67Manually introduce a bug to `SstFileWriter::Put`:
68
69```
70diff --git a/table/sst_file_writer.cc b/table/sst_file_writer.cc
71index ab1ee7c4e..c7da9ffa0 100644
72--- a/table/sst_file_writer.cc
73+++ b/table/sst_file_writer.cc
74@@ -277,6 +277,11 @@ Status SstFileWriter::Add(const Slice& user_key, const Slice& value) {
75 }
76
77 Status SstFileWriter::Put(const Slice& user_key, const Slice& value) {
78+  if (user_key.starts_with("!")) {
79+    if (value.ends_with("!")) {
80+      return Status::Corruption("bomb");
81+    }
82+  }
83   return rep_->Add(user_key, value, ValueType::kTypeValue);
84 }
85```
86
87The bug is that for `Put`, if `user_key` starts with `!` and `value` ends with `!`, then corrupt.
88
89### Run fuzz testing to catch the bug
90
91Run the fuzzer by `time ./sst_file_writer_fuzzer`.
92
93Here is the output on my machine:
94
95```
96Corruption: bomb
97==59680== ERROR: libFuzzer: deadly signal
98    #0 0x109487315 in __sanitizer_print_stack_trace+0x35 (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x4d315)
99    #1 0x108d63f18 in fuzzer::PrintStackTrace() FuzzerUtil.cpp:205
100    #2 0x108d47613 in fuzzer::Fuzzer::CrashCallback() FuzzerLoop.cpp:232
101    #3 0x7fff6af535fc in _sigtramp+0x1c (libsystem_platform.dylib:x86_64+0x35fc)
102    #4 0x7ffee720f3ef  (<unknown module>)
103    #5 0x7fff6ae29807 in abort+0x77 (libsystem_c.dylib:x86_64+0x7f807)
104    #6 0x108cf1c4c in TestOneProtoInput(DBOperations&)+0x113c (sst_file_writer_fuzzer:x86_64+0x100302c4c)
105    #7 0x108cf09be in LLVMFuzzerTestOneInput+0x16e (sst_file_writer_fuzzer:x86_64+0x1003019be)
106    #8 0x108d48ce0 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) FuzzerLoop.cpp:556
107    #9 0x108d48425 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) FuzzerLoop.cpp:470
108    #10 0x108d4a626 in fuzzer::Fuzzer::MutateAndTestOne() FuzzerLoop.cpp:698
109    #11 0x108d4b325 in fuzzer::Fuzzer::Loop(std::__1::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) FuzzerLoop.cpp:830
110    #12 0x108d37fcd in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) FuzzerDriver.cpp:829
111    #13 0x108d652b2 in main FuzzerMain.cpp:19
112    #14 0x7fff6ad5acc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)
113
114NOTE: libFuzzer has rudimentary signal handlers.
115      Combine libFuzzer with AddressSanitizer or similar for better crash reports.
116SUMMARY: libFuzzer: deadly signal
117MS: 7 Custom-CustomCrossOver-InsertByte-Custom-ChangeBit-Custom-CustomCrossOver-; base unit: 90863b4d83c3f994bba0a417d0c2ee3b68f9e795
1180x6f,0x70,0x65,0x72,0x61,0x74,0x69,0x6f,0x6e,0x73,0x20,0x7b,0xa,0x20,0x20,0x6b,0x65,0x79,0x3a,0x20,0x22,0x21,0x22,0xa,0x20,0x20,0x76,0x61,0x6c,0x75,0x65,0x3a,0x20,0x22,0x21,0x22,0xa,0x20,0x20,0x74,0x79,0x70,0x65,0x3a,0x20,0x50,0x55,0x54,0xa,0x7d,0xa,0x6f,0x70,0x65,0x72,0x61,0x74,0x69,0x6f,0x6e,0x73,0x20,0x7b,0xa,0x20,0x20,0x6b,0x65,0x79,0x3a,0x20,0x22,0x2b,0x22,0xa,0x20,0x20,0x74,0x79,0x70,0x65,0x3a,0x20,0x50,0x55,0x54,0xa,0x7d,0xa,0x6f,0x70,0x65,0x72,0x61,0x74,0x69,0x6f,0x6e,0x73,0x20,0x7b,0xa,0x20,0x20,0x6b,0x65,0x79,0x3a,0x20,0x22,0x2e,0x22,0xa,0x20,0x20,0x74,0x79,0x70,0x65,0x3a,0x20,0x50,0x55,0x54,0xa,0x7d,0xa,0x6f,0x70,0x65,0x72,0x61,0x74,0x69,0x6f,0x6e,0x73,0x20,0x7b,0xa,0x20,0x20,0x6b,0x65,0x79,0x3a,0x20,0x22,0x5c,0x32,0x35,0x33,0x22,0xa,0x20,0x20,0x74,0x79,0x70,0x65,0x3a,0x20,0x50,0x55,0x54,0xa,0x7d,0xa,
119operations {\x0a  key: \"!\"\x0a  value: \"!\"\x0a  type: PUT\x0a}\x0aoperations {\x0a  key: \"+\"\x0a  type: PUT\x0a}\x0aoperations {\x0a  key: \".\"\x0a  type: PUT\x0a}\x0aoperations {\x0a  key: \"\\253\"\x0a  type: PUT\x0a}\x0a
120artifact_prefix='./'; Test unit written to ./crash-a1460be302d09b548e61787178d9edaa40aea467
121Base64: b3BlcmF0aW9ucyB7CiAga2V5OiAiISIKICB2YWx1ZTogIiEiCiAgdHlwZTogUFVUCn0Kb3BlcmF0aW9ucyB7CiAga2V5OiAiKyIKICB0eXBlOiBQVVQKfQpvcGVyYXRpb25zIHsKICBrZXk6ICIuIgogIHR5cGU6IFBVVAp9Cm9wZXJhdGlvbnMgewogIGtleTogIlwyNTMiCiAgdHlwZTogUFVUCn0K
122./sst_file_writer_fuzzer  5.97s user 4.40s system 64% cpu 16.195 total
123```
124
125Within 6 seconds, it catches the bug.
126
127The input that triggers the bug is persisted in `./crash-a1460be302d09b548e61787178d9edaa40aea467`:
128
129```
130$ cat ./crash-a1460be302d09b548e61787178d9edaa40aea467
131operations {
132  key: "!"
133  value: "!"
134  type: PUT
135}
136operations {
137  key: "+"
138  type: PUT
139}
140operations {
141  key: "."
142  type: PUT
143}
144operations {
145  key: "\253"
146  type: PUT
147}
148```
149
150### Reproduce the crash to debug
151
152The above crash can be reproduced by `./sst_file_writer_fuzzer ./crash-a1460be302d09b548e61787178d9edaa40aea467`,
153so you can debug the crash.
154
155## Future Work
156
157According to [OSS-Fuzz](https://github.com/google/oss-fuzz),
158`as of June 2020, OSS-Fuzz has found over 20,000 bugs in 300 open source projects.`
159
160RocksDB can join OSS-Fuzz together with other open source projects such as sqlite.
161