[build][fuzzing] Generate unit tests for fuzzers
This CL adds automatically generated fuzzer tests that can be included
by adding a `fuzzer_tests_package` with the same fuzzers as the
`fuzzers_package`.
Bug: SEC-224
Test: fx run-test <fuzzer_tests_package>
Change-Id: I15729cec8e3320b5c94777befab234ff43861438
diff --git a/build/fuzzing/fuzzer.gni b/build/fuzzing/fuzzer.gni
index f00e497..51f37926 100644
--- a/build/fuzzing/fuzzer.gni
+++ b/build/fuzzing/fuzzer.gni
@@ -4,6 +4,7 @@
import("//build/host.gni")
import("//build/package.gni")
+import("//build/test.gni")
# TODO(aarongreen): SEC-224. Add tests to catch fuzzer building/packaging
# regressions.
@@ -63,6 +64,41 @@
}
}
+ # Generate a unit test for the fuzzer.
+ test("${fuzzer_name}_test") {
+ deps = []
+ forward_variables_from(invoker,
+ "*",
+ [
+ "dictionary",
+ "options",
+ "target_name",
+ "visibility",
+ ])
+
+ # Explicitly forward visibility for nested scopes.
+ forward_variables_from(invoker, [ "visibility" ])
+ deps += [
+ "//src/fuzzing/cpp:fuzzer_test",
+ "//third_party/googletest:gtest_main",
+ ]
+ }
+
+ # Generate the fuzzer test component manifest
+ action("${fuzzer_name}_test_cmx") {
+ script = "//build/fuzzing/gen_fuzzer_manifest.py"
+ outputs = [
+ "${target_gen_dir}/${fuzzer_name}_test.cmx",
+ ]
+ args = [
+ "--test",
+ "--out",
+ rebase_path(outputs[0]),
+ "--bin",
+ "${fuzzer_name}_test",
+ ]
+ }
+
# Generate data files needed at runtime
output_dictionary = "${target_gen_dir}/${fuzzer_name}/dictionary"
if (defined(invoker.dictionary)) {
@@ -117,6 +153,9 @@
# [boolean] Indicates whether to also build fuzzer binaries on host.
# Defaults to false.
#
+# generated_test_package (optional)
+# [string] The name of a package of fuzzer tests to create.
+#
# meta (optional)
# binaries (optional)
# components (optional)
@@ -156,9 +195,12 @@
binaries = []
resources = []
deps = []
+ fuzzers = []
host_deps = []
host_outputs = []
- fuzzers = []
+ tests = []
+ test_meta = []
+ test_deps = []
}
# Collect the selected fuzzers listed in this package based on the variants
@@ -166,6 +208,9 @@
# sanitizers.
foreach(fuzzer, invoker.fuzzers) {
selected = false
+ fuzzer_name = get_label_info(fuzzer, "name")
+ fuzzer_path = get_label_info(fuzzer, "target_gen_dir")
+ fuzzer_label = get_label_info(fuzzer, "label_no_toolchain")
foreach(sanitizer, sanitizers) {
foreach(selector, select_variant_canonical) {
if (!selected && selector.variant == "${sanitizer}-fuzzer" &&
@@ -175,9 +220,8 @@
(defined(selector.output_name) &&
selector.output_name == [ fuzzer ]))) {
selected = true
- fuzzer_name = get_label_info(fuzzer, "name")
- fuzzer_path = get_label_info(fuzzer, "target_gen_dir")
- fuzzer_label = get_label_info(fuzzer, "label_no_toolchain")
+
+ # Package details.
pkg.meta += [
{
path = "${fuzzer_path}/${fuzzer_name}.cmx"
@@ -205,15 +249,34 @@
"${fuzzer_label}_dictionary",
"${fuzzer_label}_options",
]
+ pkg.fuzzers += [ fuzzer_name ]
+
+ # Host fuzzers
if (fuzz_host) {
pkg.deps += [ ":host_${target_name}" ]
pkg.host_deps += [ fuzzer_label ]
pkg.host_outputs += [ fuzzer_name ]
}
- pkg.fuzzers += [ fuzzer_name ]
}
}
}
+
+ # Fuzzer tests
+ pkg.tests += [
+ {
+ name = "${fuzzer_name}_test"
+ },
+ ]
+ pkg.test_meta += [
+ {
+ path = "${fuzzer_path}/${fuzzer_name}_test.cmx"
+ dest = "${fuzzer_name}_test.cmx"
+ },
+ ]
+ pkg.test_deps += [
+ "${fuzzer_label}_test",
+ "${fuzzer_label}_test_cmx",
+ ]
not_needed([ "selected" ])
}
@@ -228,6 +291,7 @@
"*",
[
"fuzzers",
+ "generated_test_package",
"metadata",
"sanitizers",
"visibility",
@@ -277,6 +341,19 @@
"fuzz_host",
])
}
+
+ # Assemble the Fuchsia test package. This uses `package` instead of
+ # `test_package`, as the latter only provides an inconvenient constraint on
+ # where the cmx file must come from.
+ if (defined(invoker.generated_test_package)) {
+ package(invoker.generated_test_package) {
+ forward_variables_from(invoker, [ "visibility" ])
+ testonly = true
+ meta = pkg.test_meta
+ tests = pkg.tests
+ deps = pkg.test_deps
+ }
+ }
}
# TODO(aarongreen): Complete soft transition and remove.
diff --git a/build/fuzzing/gen_fuzzer_manifest.py b/build/fuzzing/gen_fuzzer_manifest.py
index b9d45bc..165afe4 100755
--- a/build/fuzzing/gen_fuzzer_manifest.py
+++ b/build/fuzzing/gen_fuzzer_manifest.py
@@ -17,6 +17,10 @@
help="Path to the binary; absolute or relative to package's bin directory",
required=True)
parser.add_argument("--cmx", help="Optional starting manifest")
+ parser.add_argument(
+ "--test",
+ action="store_true",
+ help="Generate manifest for the fuzzer test package.")
args = parser.parse_args()
cmx = defaultdict(dict)
@@ -24,21 +28,18 @@
with open(args.cmx, "r") as f:
cmx = json.load(f)
- if args.bin.startswith("/"):
- # Zircon fuzz_targets are absolute paths in bootfs.
- cmx["program"]["binary"] = args.bin
+ if args.test:
+ cmx["program"]["binary"] = "test/" + args.bin
else:
- # Fuchsia fuzz_targets are part of a package
cmx["program"]["binary"] = "bin/" + args.bin
+ if "services" not in cmx["sandbox"]:
+ cmx["sandbox"]["services"] = []
+ cmx["sandbox"]["services"].append("fuchsia.process.Launcher")
- if "services" not in cmx["sandbox"]:
- cmx["sandbox"]["services"] = []
- cmx["sandbox"]["services"].append("fuchsia.process.Launcher")
-
- if "features" not in cmx["sandbox"]:
- cmx["sandbox"]["features"] = []
- if "isolated-persistent-storage" not in cmx["sandbox"]["features"]:
- cmx["sandbox"]["features"].append("isolated-persistent-storage")
+ if "features" not in cmx["sandbox"]:
+ cmx["sandbox"]["features"] = []
+ if "isolated-persistent-storage" not in cmx["sandbox"]["features"]:
+ cmx["sandbox"]["features"].append("isolated-persistent-storage")
with open(args.out, "w") as f:
f.write(json.dumps(cmx, sort_keys=True, indent=4))
diff --git a/src/connectivity/BUILD.gn b/src/connectivity/BUILD.gn
index 3914575..9dcd505 100644
--- a/src/connectivity/BUILD.gn
+++ b/src/connectivity/BUILD.gn
@@ -18,6 +18,7 @@
data_deps = [
"//src/connectivity/bluetooth/core/bt-gap:tests",
+ "//src/connectivity/bluetooth/core/bt-host:bluetooth_fuzzers_test",
"//src/connectivity/bluetooth/lib/bt-avdtp:tests",
"//src/connectivity/bluetooth/lib/fuchsia-bluetooth:tests",
"//src/connectivity/bluetooth/profiles/bt-a2dp-sink:tests",
@@ -27,6 +28,7 @@
"//src/connectivity/network/testing/netemul:tests",
"//src/connectivity/overnet/examples:tests",
"//src/connectivity/overnet/lib:overnet_tests",
+ "//src/connectivity/overnet/lib:overnet_fuzzer_tests",
"//src/connectivity/overnet/overnetstack:overnetstack_tests",
"//src/connectivity/telephony/lib/qmi-protocol:tests",
"//src/connectivity/wlan:tests",
diff --git a/src/connectivity/bluetooth/core/bt-host/BUILD.gn b/src/connectivity/bluetooth/core/bt-host/BUILD.gn
index fe75acb..703d419 100644
--- a/src/connectivity/bluetooth/core/bt-host/BUILD.gn
+++ b/src/connectivity/bluetooth/core/bt-host/BUILD.gn
@@ -124,4 +124,5 @@
"l2cap:basic_mode_rx_engine_fuzzer",
"l2cap:enhanced_retransmission_mode_rx_engine_fuzzer",
]
+ generated_test_package = "bluetooth_fuzzers_test"
}
diff --git a/src/connectivity/overnet/lib/BUILD.gn b/src/connectivity/overnet/lib/BUILD.gn
index 8ce1c90..e645b12 100644
--- a/src/connectivity/overnet/lib/BUILD.gn
+++ b/src/connectivity/overnet/lib/BUILD.gn
@@ -71,4 +71,5 @@
]
sanitizers = [ "asan" ]
fuzz_host = true
+ generated_test_package = "overnet_fuzzer_tests"
}
diff --git a/src/fuzzing/BUILD.gn b/src/fuzzing/BUILD.gn
index bdd572d..4207432 100644
--- a/src/fuzzing/BUILD.gn
+++ b/src/fuzzing/BUILD.gn
@@ -8,17 +8,6 @@
group("tests") {
testonly = true
public_deps = [
- ":fuzzing_host_test($host_toolchain)",
- ]
-}
-
-go_library("fuzzing_host_test_lib") {
- name = "fuzzing"
-}
-
-go_test("fuzzing_host_test") {
- gopackage = "fuzzing"
- deps = [
- ":fuzzing_host_test_lib",
+ "host:tests",
]
}
diff --git a/src/fuzzing/cpp/BUILD.gn b/src/fuzzing/cpp/BUILD.gn
new file mode 100644
index 0000000..b839040
--- /dev/null
+++ b/src/fuzzing/cpp/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("fuzzer_test") {
+ testonly = true
+ sources = [
+ "fuzzer_test.cc",
+ ]
+ deps = [
+ "//src/lib/files",
+ "//third_party/googletest:gtest",
+ ]
+}
diff --git a/src/fuzzing/cpp/fuzzer_test.cc b/src/fuzzing/cpp/fuzzer_test.cc
new file mode 100644
index 0000000..3b4b8c7
--- /dev/null
+++ b/src/fuzzing/cpp/fuzzer_test.cc
@@ -0,0 +1,35 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "src/lib/files/directory.h"
+#include "src/lib/files/file.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+TEST(FuzzerTest, EmptyInput) {
+ EXPECT_EQ(0, LLVMFuzzerTestOneInput(nullptr, 0));
+}
+
+// TODO(aarongreen): Placeholder for now, until we figure out how we want to
+// plumb the corpora from CIPD through to images built for test in CQ.
+const char *kCorpusDir = "data/corpus";
+TEST(FuzzerTest, WithCorpus) {
+ if (!files::IsDirectory(kCorpusDir)) {
+ return;
+ }
+ std::vector<std::string> inputs;
+ std::vector<uint8_t> data;
+ ASSERT_TRUE(files::ReadDirContents(kCorpusDir, &inputs));
+ for (auto input : inputs) {
+ ASSERT_TRUE(files::ReadFileToVector(input, &data));
+ EXPECT_EQ(0, LLVMFuzzerTestOneInput(&data[0], data.size()));
+ }
+}
diff --git a/src/fuzzing/host/BUILD.gn b/src/fuzzing/host/BUILD.gn
new file mode 100644
index 0000000..e2c7b80
--- /dev/null
+++ b/src/fuzzing/host/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/go/go_library.gni")
+import("//build/go/go_test.gni")
+
+group("tests") {
+ testonly = true
+ public_deps = [
+ ":host_fuzzers_test($host_toolchain)",
+ ]
+}
+
+go_library("host_fuzzers_test_lib") {
+ name = "fuzzing"
+}
+
+go_test("host_fuzzers_test") {
+ gopackage = "fuzzing"
+ deps = [
+ ":host_fuzzers_test_lib",
+ ]
+}
diff --git a/src/fuzzing/host_test.go b/src/fuzzing/host/host_test.go
similarity index 100%
rename from src/fuzzing/host_test.go
rename to src/fuzzing/host/host_test.go
diff --git a/src/ledger/BUILD.gn b/src/ledger/BUILD.gn
index 0962b0e..eaf54bc 100644
--- a/src/ledger/BUILD.gn
+++ b/src/ledger/BUILD.gn
@@ -22,9 +22,9 @@
group("tests") {
testonly = true
-
- data_deps = [
+ deps = [
":ledger_tests",
+ "bin:ledger_fuzzer_tests",
]
}
diff --git a/src/ledger/bin/BUILD.gn b/src/ledger/bin/BUILD.gn
index 2f0a696..6361e1c 100644
--- a/src/ledger/bin/BUILD.gn
+++ b/src/ledger/bin/BUILD.gn
@@ -59,4 +59,5 @@
"//src/ledger/bin/p2p_sync/impl:p2p_sync_fuzzer",
"//src/ledger/bin/storage/impl/btree:encoding_fuzzer",
]
+ generated_test_package = "ledger_fuzzer_tests"
}