Skip to content

Commit 998f7c0

Browse files
authored
Fuzzer (#114)
1 parent 59e2ab2 commit 998f7c0

10 files changed

Lines changed: 500 additions & 5 deletions

File tree

.bazelrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,10 @@ build:msan --linkopt="-fsanitize=memory"
111111
test:msan --run_under=//tools/runners/sanitizers/msan
112112

113113
build:lint --define linting_only=true
114+
115+
build:fuzz --action_env=CC=clang
116+
build:fuzz --action_env=CXX=clang++
117+
build:fuzz --config=base-sanitizer
118+
build:fuzz --copt="-g"
119+
build:fuzz --copt="-fsanitize=fuzzer"
120+
build:fuzz --linkopt="-fsanitize=fuzzer"

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
88

99
### Added
1010
- Added B+tree multimap for internal (future) use. [#93](https://github.com/tzaeschke/phtree-cpp/issues/93)
11+
- Added some fuzz tests. Not that these require manual compilation, see [fuzzer/README.md](fuzzer/README.md).
12+
[#114](https://github.com/tzaeschke/phtree-cpp/pull/114)
1113

1214
### Changed
1315
- Clean up array_map. [#107](https://github.com/tzaeschke/phtree-cpp/issues/107),

fuzzer/BUILD

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package(default_visibility = ["//visibility:private"])
2+
3+
#cc_binary(
4+
# name = "b_plus_multimap_fuzzer",
5+
# srcs = [
6+
# "b_plus_multimap_fuzzer.cpp",
7+
# ],
8+
# linkstatic = True,
9+
# deps = [
10+
# "//:phtree",
11+
# ],
12+
#)

fuzzer/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Fuzzing
2+
3+
4+
Requirements:
5+
* `clang`.
6+
* libFuzzer: https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md
7+
8+
Compile one of:
9+
* `clang++ -g -std=c++17 -fsanitize=fuzzer fuzzer/b_plus_multimap_fuzzer.cc -I.`
10+
* `clang++ -g -std=c++17 -fsanitize=fuzzer fuzzer/b_plus_map_fuzzer.cc -I.`
11+
* `clang++ -g -std=c++17 -fsanitize=fuzzer fuzzer/b_plus_hash_map_fuzzer.cc -I.`
12+
13+
Execute:
14+
* `./a.out`
15+
* `./a.out -minimize_crash=1 -runs=10000 /tmp/tmp.b521097a4f49`
16+
* `./a.out /tmp/tmp.12345678/artifacts/minimized-from-185ecf42f208c2a7736a98ba0403f31868bcb681`
17+
18+
To give an artifact path:
19+
* `-artifact_prefix=/home/my-name/tmp/fuzz/artifacts/`
20+
21+
## Bazel
22+
23+
Fuzzing with bazel is possible but is currently disabled because it breaks `bazel build ...`.
24+
25+
* We would need to set `clang`/`clang++` as compiler (`gcc` would not work anymore)
26+
* We would need to solve the problem that `bazel build ...` fails unless `-fsanitize=fuzzer` is set
27+
28+
### Using a simple executable
29+
Uncomment build rules in BUILD file, then:
30+
`CC=clang bazel run //fuzzer:b_plus_multimap_fuzzer --config=fuzz`
31+
32+
### Using the bazel cc_fuzz_test
33+
https://github.com/bazelbuild/rules_fuzzing/blob/master/docs/guide.md

fuzzer/b_plus_hash_map_fuzzer.cc

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright 2023 Tilmann Zäschke
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <assert.h>
18+
#include <cstddef>
19+
#include <cstdint>
20+
#include <iostream>
21+
#include <ostream>
22+
23+
#include <map>
24+
25+
#include "include/phtree/common/b_plus_tree_hash_map.h"
26+
27+
static volatile int Sink;
28+
29+
using Instruction = std::uint8_t;
30+
using Key = std::uint8_t;
31+
using Value = std::uint8_t;
32+
33+
constexpr bool PRINT = !true;
34+
35+
void print() {}
36+
37+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
38+
assert(Data);
39+
40+
if (PRINT) {
41+
std::cout << "TEST(PhTreeBptMapTest, FuzzTest1) {" << std::endl;
42+
std::cout << " using Key = std::uint8_t;" << std::endl;
43+
std::cout << " using Value = std::uint8_t;" << std::endl;
44+
std::cout << " b_plus_tree_map<Key, Value, 256> tree{};" << std::endl;
45+
}
46+
47+
auto scopeguard = []() { std::cout << "};" << std::endl; };
48+
49+
improbable::phtree::b_plus_tree_hash_map<Key, Value> tree;
50+
std::map<Key, Value> map;
51+
52+
size_t pos = 0;
53+
54+
while (pos + 4 < Size) {
55+
Instruction inst = Data[pos++] % 4;
56+
Key key = Data[pos++];
57+
Value value = Data[pos++];
58+
switch (inst) {
59+
case 0: {
60+
if (PRINT)
61+
std::cout << " tree.emplace(" << (int)key << ", " << (int)value << ");"
62+
<< std::endl;
63+
tree.emplace(key, value);
64+
map.emplace(key, value);
65+
break;
66+
}
67+
case 1: {
68+
if (PRINT)
69+
std::cout << " tree.erase(" << (int)key << ");" << std::endl;
70+
tree.erase(key);
71+
map.erase(key);
72+
break;
73+
}
74+
case 2: {
75+
if (PRINT)
76+
std::cout << " auto it = tree.find(" << (int)key << ");" << std::endl;
77+
auto it = tree.find(key);
78+
if (it != tree.end()) {
79+
if (PRINT)
80+
std::cout << " tree.erase(it);" << std::endl;
81+
tree.erase(it);
82+
}
83+
auto it2 = map.find(key);
84+
if (it2 != map.end()) {
85+
map.erase(it2);
86+
}
87+
break;
88+
}
89+
case 3: {
90+
if (PRINT)
91+
std::cout << " auto it = tree.lower_bound(" << (int)key << ");" << std::endl;
92+
auto it = tree.lower_bound(key);
93+
if (PRINT)
94+
std::cout << " tree.emplace_hint(it, " << (int)key << ", " << (int)value << ");"
95+
<< std::endl;
96+
tree.emplace_hint(it, key, value);
97+
auto it2 = map.lower_bound(key);
98+
map.emplace_hint(it2, key, value);
99+
break;
100+
}
101+
default:
102+
std::cout << "Unexpected instruction: " << inst << std::endl;
103+
}
104+
}
105+
106+
tree._check();
107+
108+
for (auto& entry : map) {
109+
const Key& vRef = entry.first;
110+
Key vMap = tree.find(vRef)->first;
111+
assert(vMap == vRef);
112+
}
113+
for (auto& entry : tree) {
114+
Key v = entry.first;
115+
const Key& vRef = map.find(v)->first;
116+
Key vMap = tree.find(v)->first;
117+
assert(vMap == vRef);
118+
}
119+
assert(tree.size() == map.size());
120+
121+
return 0;
122+
}

fuzzer/b_plus_map_fuzzer.cc

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright 2023 Tilmann Zäschke
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <assert.h>
18+
#include <cstddef>
19+
#include <cstdint>
20+
#include <iostream>
21+
#include <ostream>
22+
23+
#include <map>
24+
25+
#include "include/phtree/common/b_plus_tree_map.h"
26+
27+
static volatile int Sink;
28+
29+
using Instruction = std::uint8_t;
30+
using Key = std::uint8_t;
31+
using Value = std::uint8_t;
32+
33+
constexpr bool PRINT = !true;
34+
35+
void print() {}
36+
37+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
38+
assert(Data);
39+
40+
if (PRINT) {
41+
std::cout << "TEST(PhTreeBptMapTest, FuzzTest1) {" << std::endl;
42+
std::cout << " using Key = std::uint8_t;" << std::endl;
43+
std::cout << " using Value = std::uint8_t;" << std::endl;
44+
std::cout << " b_plus_tree_map<Key, Value, 256> tree{};" << std::endl;
45+
}
46+
47+
auto scopeguard = []() { std::cout << "};" << std::endl; };
48+
49+
improbable::phtree::b_plus_tree_map<Key, Value, 256> tree;
50+
std::map<Key, Value> map;
51+
52+
size_t pos = 0;
53+
54+
while (pos + 4 < Size) {
55+
Instruction inst = Data[pos++] % 4;
56+
Key key = Data[pos++];
57+
Value value = Data[pos++];
58+
switch (inst) {
59+
case 0: {
60+
if (PRINT)
61+
std::cout << " tree.emplace(" << (int)key << ", " << (int)value << ");"
62+
<< std::endl;
63+
tree.emplace(key, value);
64+
map.emplace(key, value);
65+
break;
66+
}
67+
case 1: {
68+
if (PRINT)
69+
std::cout << " tree.erase(" << (int)key << ");" << std::endl;
70+
tree.erase(key);
71+
map.erase(key);
72+
break;
73+
}
74+
case 2: {
75+
if (PRINT)
76+
std::cout << " auto it = tree.find(" << (int)key << ");" << std::endl;
77+
auto it = tree.find(key);
78+
if (it != tree.end()) {
79+
if (PRINT)
80+
std::cout << " tree.erase(it);" << std::endl;
81+
tree.erase(it);
82+
}
83+
auto it2 = map.find(key);
84+
if (it2 != map.end()) {
85+
map.erase(it2);
86+
}
87+
break;
88+
}
89+
case 3: {
90+
if (PRINT)
91+
std::cout << " auto it = tree.lower_bound(" << (int)key << ");" << std::endl;
92+
auto it = tree.lower_bound(key);
93+
if (PRINT)
94+
std::cout << " tree.emplace_hint(it, " << (int)key << ", " << (int)value << ");"
95+
<< std::endl;
96+
tree.emplace_hint(it, key, value);
97+
auto it2 = map.lower_bound(key);
98+
map.emplace_hint(it2, key, value);
99+
break;
100+
}
101+
default:
102+
std::cout << "Unexpected instruction: " << inst << std::endl;
103+
}
104+
}
105+
106+
tree._check();
107+
108+
for (auto& entry : map) {
109+
const Key& vRef = entry.first;
110+
Key vMap = tree.find(vRef)->first;
111+
assert(vMap == vRef);
112+
}
113+
for (auto& entry : tree) {
114+
Key v = entry.first;
115+
const Key& vRef = map.find(v)->first;
116+
Key vMap = tree.find(v)->first;
117+
assert(vMap == vRef);
118+
}
119+
assert(tree.size() == map.size());
120+
121+
return 0;
122+
}

0 commit comments

Comments
 (0)