pw_toolchain: Add OSS-Fuzz support
This CL adds an GN arg to set `oss_fuzz_enabled`. When true, the clang
toolchains will use environment variables like CC and CFLAGS passed by
OSS-Fuzz, as documented in
https://google.github.io/oss-fuzz/getting-started/new-project-guide/#buildsh
Additionally, it moves the `pw_sanitizer` from pw_fuzzer to the same arg
scope, as that argument should configure clang independently from
fuzzing.
Finally, it adds some documentation for how to use OSS-Fuzz outputs.
Change-Id: I0ca316d498d9dcd96156cbf19009eb734c3986c1
diff --git a/pw_fuzzer/BUILD.gn b/pw_fuzzer/BUILD.gn
index 5a43021..dec32bd 100644
--- a/pw_fuzzer/BUILD.gn
+++ b/pw_fuzzer/BUILD.gn
@@ -15,12 +15,6 @@
import("$dir_pw_docgen/docs.gni")
import("$dir_pw_fuzzer/fuzzer.gni")
-declare_args() {
- # Sets the sanitizer to pass to clang. Valid values are those for "-fsanitize"
- # listed in https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html.
- pw_sanitizer = ""
-}
-
config("default_config") {
include_dirs = [ "public" ]
}
diff --git a/pw_fuzzer/docs.rst b/pw_fuzzer/docs.rst
index 355e2ff..e383566 100644
--- a/pw_fuzzer/docs.rst
+++ b/pw_fuzzer/docs.rst
@@ -64,6 +64,8 @@
those **only** when fuzzing by using LLVM's
`FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION`_
+.. _build:
+
Building fuzzers
================
@@ -98,8 +100,10 @@
3. Build normally, e.g. using ``pw watch``.
-Running fuzzers
-===============
+.. _run:
+
+Running fuzzers locally
+=======================
Based on the example above, the fuzzer output will be at
``out/host/obj/my_module/my_fuzzer``. It can be invoked using the normal
@@ -157,9 +161,61 @@
MS: 1 CrossOver-; base unit: 9f479f7a6e3a21363397a25da3168218ba182a16
0x68,0x65,0x6c,0x6c,0x6f,0x0,0x77,0x6f,0x72,0x6c,0x64,0x0,0x0,0x0,
hello\x00world\x00\x00\x00
- artifact_prefix='artifacts'; Test unit written to artifactscrash-6e4fdc7ffd04131ea15dd243a0890b1b606f4831
+ artifact_prefix='artifacts'; Test unit written to artifacts/crash-6e4fdc7ffd04131ea15dd243a0890b1b606f4831
Base64: aGVsbG8Ad29ybGQAAAA=
+Running fuzzers on OSS-Fuzz
+===========================
+
+Pigweed is integrated with `OSS-Fuzz`_, a continuous fuzzing infrastructure for
+open source software. Fuzzers listed in in ``pw_test_groups`` will automatically
+start being run within a day or so of appearing in the git repository.
+
+Bugs produced by OSS-Fuzz can be found in its `Monorail instance`_. These bugs
+include:
+
+* A detailed report, including a symbolized backtrace.
+* A revision range indicating when the bug has been detected.
+* A minimized testcase, which is a fuzzer input that can be used to reproduce
+ the bug.
+
+To reproduce a bug:
+
+#. Build_ the fuzzers as described above.
+#. Download the minimized testcase.
+#. Run_ the fuzzer with the testcase as an argument.
+
+For example, if the testcase is saved as "~/Downloads/testcase"
+and the fuzzer is the same as in the examples above, you could run:
+
+.. code::
+
+ $ ./out/host/obj/pw_fuzzer/toy_fuzzer ~/Downloads/testcase
+
+If you need to recreate the OSS-Fuzz environment locally, you can use its
+documentation on `reproducing`_ issues.
+
+In particular, you can recreate the OSS-Fuzz environment using:
+
+.. code::
+
+ $ python infra/helper.py pull_images
+ $ python infra/helper.py build_image pigweed
+ $ python infra/helper.py build_fuzzers --sanitizer <address/undefined> pigweed
+
+With that environment, you can run the reproduce bugs using:
+
+.. code::
+
+ python infra/helper.py reproduce pigweed <pw_module>_<fuzzer_name> ~/Downloads/testcase
+
+You can even verify fixes in your local source checkout:
+
+.. code::
+
+ $ python infra/helper.py build_fuzzers --sanitizer <address/undefined> pigweed $PW_ROOT
+ $ python infra/helper.py reproduce pigweed <pw_module>_<fuzzer_name> ~/Downloads/testcase
+
.. _compiler_rt: https://compiler-rt.llvm.org/
.. _corpus: https://llvm.org/docs/LibFuzzer.html#corpus
.. _FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION: https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode
@@ -167,6 +223,9 @@
.. _libFuzzer: https://llvm.org/docs/LibFuzzer.html
.. _libFuzzer options: https://llvm.org/docs/LibFuzzer.html#options
.. _LLVMFuzzerTestOneInput: https://llvm.org/docs/LibFuzzer.html#fuzz-target
+.. _monorail instance: https://bugs.chromium.org/p/oss-fuzz
+.. _oss-fuzz: https://github.com/google/oss-fuzz
+.. _reproducing: https://google.github.io/oss-fuzz/advanced-topics/reproducing/
.. _running a fuzzer: https://llvm.org/docs/LibFuzzer.html#running
.. _sanitizer runtime flags: https://github.com/google/sanitizers/wiki/SanitizerCommonFlags
.. _split a fuzzing input: https://github.com/google/fuzzing/blob/master/docs/split-inputs.md
diff --git a/pw_fuzzer/fuzzer.gni b/pw_fuzzer/fuzzer.gni
index a123e0d..73461da 100644
--- a/pw_fuzzer/fuzzer.gni
+++ b/pw_fuzzer/fuzzer.gni
@@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations under
# the License.
+import("$dir_pw_toolchain/host_clang.gni")
import("$dir_pw_unit_test/test.gni")
# Creates a libFuzzer-based fuzzer executable target.
diff --git a/pw_toolchain/host_clang.gni b/pw_toolchain/host_clang.gni
index f4d2803..a842622 100644
--- a/pw_toolchain/host_clang.gni
+++ b/pw_toolchain/host_clang.gni
@@ -12,6 +12,17 @@
# License for the specific language governing permissions and limitations under
# the License.
+declare_args() {
+ # Sets the sanitizer to pass to clang. Valid values are those for "-fsanitize"
+ # listed in https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html.
+ pw_sanitizer = ""
+
+ # Indicates if this build is a part of OSS-Fuzz, which needs to be able to
+ # provide its own compiler and flags. This violates the build hermeticisim and
+ # should only be used for OSS-Fuzz.
+ oss_fuzz_enabled = false
+}
+
# Generates a host clang toolchain for a specific target.
#
# Args:
@@ -40,13 +51,17 @@
_toolchain_cflags = string_join(" ", _cflags_list)
- # Specify the default C++ version, which targets can override with a config.
- _toolchain_cflags_cc = "-std=c++17 -Wno-register"
+ # Toolchain C++ flags
+ _cflags_cc_list = [
+ # Specify the default C++ version, which targets can override with a config.
+ "-std=c++17",
+ "-Wno-register",
+ ]
# Toolchain LD flags
- _toolchain_ldflags = ""
+ _ldflags_list = []
if (defined(invoker.toolchain_ldflags)) {
- _toolchain_ldflags = string_join(" ", invoker.toolchain_ldflags)
+ _ldflags_list += invoker.toolchain_ldflags
}
if (host_os == "mac") {
# The CIPD provided Clang/LLVM toolchain must link against the matched
@@ -58,13 +73,13 @@
assert(getenv("PW_PIGWEED_CIPD_INSTALL_DIR") != "",
"You forgot to activate the Pigweed environment; " +
"did you source pw_env_setup/setup.sh?")
+ _ldflags_list += [
+ # Force dropping the system libc++
+ "-nostdlib++",
- # Force dropping the system libc++
- _toolchain_ldflags += "-nostdlib++ "
-
- # Use the libc++ from CIPD.
- _toolchain_ldflags += getenv("PW_PIGWEED_CIPD_INSTALL_DIR")
- _toolchain_ldflags += "/lib/libc++.a"
+ # Use the libc++ from CIPD.
+ getenv("PW_PIGWEED_CIPD_INSTALL_DIR") + "/lib/libc++.a",
+ ]
}
# Note: On macOS, there is no "llvm-ar", only "ar", which happens to be LLVM
@@ -73,6 +88,19 @@
_cc = "clang"
_cxx = "clang++"
+ # OSS-Fuzz needs to be able to specify its own compilers and add flags.
+ if (oss_fuzz_enabled) {
+ _cc = getenv("CC")
+ _cxx = getenv("CXX")
+ _cflags_list += string_split(getenv("CFLAGS"))
+ _cflags_cc_list += string_split(getenv("CXXFLAGS"))
+ _ldflags_list += string_split(getenv("LDFLAGS"))
+ }
+
+ _toolchain_cflags = string_join(" ", _cflags_list)
+ _toolchain_cflags_cc = string_join(" ", _cflags_cc_list)
+ _toolchain_ldflags = string_join(" ", _ldflags_list)
+
toolchain(target_name) {
tool("asm") {
depfile = "{{output}}.d"