Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration of pw_fuzzer using FuzzTest #34274

Merged
merged 20 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/.wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ AdvSendAdvert
AE
aef
AES
AFL
AIDL
algs
alloc
Expand Down Expand Up @@ -570,6 +571,7 @@ fsync
ftd
fullclean
fuzzer
fuzztest
FW
gbl
gcloud
Expand Down Expand Up @@ -1008,6 +1010,7 @@ optionOverride
optionsMask
optionsOverride
orgs
OSS
OTA
OTADownloader
otaDownloadPath
Expand Down
12 changes: 12 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -329,3 +329,15 @@
path = third_party/infineon/psoc6/psoc6_sdk/libs/lwip-network-interface-integration
url = https://github.com/Infineon/lwip-network-interface-integration.git
platforms = infineon
[submodule "third_party/abseil-cpp/src"]
path = third_party/abseil-cpp/src
url = https://github.com/abseil/abseil-cpp.git
[submodule "third_party/fuzztest"]
path = third_party/fuzztest
url = https://github.com/google/fuzztest.git
[submodule "third_party/googletest"]
path = third_party/googletest
url = https://github.com/google/googletest
[submodule "third_party/re2/src"]
path = third_party/re2/src
url = https://github.com/google/re2.git
16 changes: 16 additions & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") {
}
}

if (pw_enable_fuzz_test_targets) {
group("pw_fuzz_tests") {
deps = [
"${chip_root}/src/credentials/tests:fuzz-chip-cert-pw(//build/toolchain/pw_fuzzer:chip_pw_fuzztest)",
"${chip_root}/src/lib/core/tests:fuzz-tlv-reader-pw(//build/toolchain/pw_fuzzer:chip_pw_fuzztest)",
"${chip_root}/src/lib/dnssd/minimal_mdns/tests:fuzz-minmdns-packet-parsing-pw(//build/toolchain/pw_fuzzer:chip_pw_fuzztest)",
"${chip_root}/src/lib/format/tests:fuzz-payload-decoder-pw(//build/toolchain/pw_fuzzer:chip_pw_fuzztest)",
"${chip_root}/src/setup_payload/tests:fuzz-setup-payload-base38-pw(//build/toolchain/pw_fuzzer:chip_pw_fuzztest)",
]
}
}

# Matter's in-tree pw_python_package or pw_python_distribution targets.
_matter_python_packages = [
"//examples/chef",
Expand Down Expand Up @@ -140,6 +152,10 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") {
deps += [ "//:fuzz_tests" ]
}

if (pw_enable_fuzz_test_targets) {
deps += [ "//:pw_fuzz_tests" ]
}

if (chip_device_platform != "none") {
deps += [ "${chip_root}/src/app/server" ]
}
Expand Down
70 changes: 70 additions & 0 deletions build/chip/fuzz_test.gni
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@

import("//build_overrides/build.gni")
import("//build_overrides/chip.gni")
import("//build_overrides/pigweed.gni")

import("${build_root}/config/compiler/compiler.gni")
import("${chip_root}/build/chip/tests.gni")
import("${dir_pw_unit_test}/test.gni")

declare_args() {
enable_fuzz_test_targets = is_clang && chip_build_tests &&
(current_os == "linux" || current_os == "mac")

pw_enable_fuzz_test_targets = false
}

# Define a fuzz target for chip.
Expand Down Expand Up @@ -66,3 +71,68 @@ template("chip_fuzz_target") {
}
}
}

# Define a fuzz target for chip using pw_fuzzer and Google FuzzTest Framework.
#
# Fuzz generally only apply on the following environments:
# - linux and mac host builds when using clang
#
# Sample usage
#
# chip_pw_fuzz_target("fuzz-target-name") {
# test_source = [
# "FuzzTarget.cpp", # Fuzz target
# ]
#
# public_deps = [
# "${chip_root}/src/lib/foo", # add dependencies here
# ]
# }
#
#
template("chip_pw_fuzz_target") {
if (!defined(invoker.sources)) {
sources = []
}
if (!defined(invoker.public_deps)) {
public_deps = []
}
if (!defined(invoker.deps)) {
deps = []
}

if (defined(invoker.test_source)) {
_test_output_dir = "${root_out_dir}/tests"

if (defined(invoker.output_dir)) {
_test_output_dir = invoker.output_dir
}

pw_test(target_name) {
forward_variables_from(invoker,
[
"deps",
"public_deps",
"cflags",
"configs",
"remove_configs",
])

# TODO: remove this after pw_fuzzer implementation its integration with OSS-Fuzz is complete
#just a test for running FuzzTest with libfuzzer-compatibility mode, since this is the mode supported by OSS-fuzz
# defines = [
# "FUZZTEST_COMPATIBILITY_MODE=libfuzzer",
# "MAKE_BUILD_TYPE=RelWithDebug",
# ]

sources = invoker.test_source
output_dir = _test_output_dir

deps += [ "$dir_pw_fuzzer:fuzztest" ]

# this is necessary so FuzzTest is compiled into an executable in third_party/pigweed/repo/pw_unit_test/test.gni
# otherwise it will be built successfully but with FuzzTarget.DISABLED.ninja and no executable.
enable_if = true
}
}
}
2 changes: 2 additions & 0 deletions build/config/BUILDCONFIG.gn
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ _chip_defaults = {
declare_args() {
# Toolchain to use for host. This is usually set by default.
host_toolchain = ""

is_pw_fuzz = false
}

if (host_toolchain == "") {
Expand Down
70 changes: 70 additions & 0 deletions build/toolchain/pw_fuzzer/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright (c) 2024 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import("//build_overrides/build.gni")
import("//build_overrides/pigweed.gni")

import("$dir_pigweed/targets/host/target_toolchains.gni")
import("${build_root}/toolchain/gcc_toolchain.gni")

# creating a secondary toolchain to be used with pw_fuzzer FuzzTests
# This toolchain is downstreamed from pigweed's pw_target_toolchain_host.clang_fuzz
# it allows us to specifically use googletest for fuzzing (instead of the lighter version of googletest used for unit testing)

gcc_toolchain("chip_pw_fuzztest") {
forward_variables_from(pw_target_toolchain_host.clang_fuzz, "*", [ "name" ])

toolchain_args = {
#This is needed to have the defaults passed from pw_target_toolchain_host.clang_fuzz to the current scope
forward_variables_from(defaults, "*")

pw_unit_test_MAIN = "$dir_pw_fuzzer:fuzztest_main"
pw_unit_test_BACKEND = "$dir_pw_fuzzer:gtest"

# below three lines are needed by gcc_toolchain template
current_os = host_os
current_cpu = host_cpu
is_clang = true

# the upstream pigweed host_clang toolchain defines a default sysroot, which results in build errors
# since it does not include SSL lib and is supposed to be minimal by design.
# by removing this default config, we will use the system's libs. Otherwise we can define our own sysroot.
# discussion on: https://discord.com/channels/691686718377558037/1275092695764959232
remove_default_configs = [ "$dir_pw_toolchain/host_clang:linux_sysroot" ]

#when is_debug = True, we pass -O0 to cflags and ldflags, while upstream pw_fuzzer toolchain defines "optimize_speed" config that passes -O2.
# This condition was added to prevent mixing the flags
if (is_debug) {
remove_default_configs += [ "$dir_pw_build:optimize_speed" ]
}

# removing pigweed downstreamed configs related to warnings
# These are triggering an error related to -Wcast-qual in third_party/nlio
remove_default_configs += [
"$dir_pw_build:strict_warnings",
"$dir_pw_build:extra_strict_warnings",
]

# the third_party abseil-cpp triggers warnings related to [-Wformat-nonliteral]
treat_warnings_as_errors = false

dir_pw_third_party_abseil_cpp = "//third_party/abseil-cpp/src"
dir_pw_third_party_fuzztest = "//third_party/fuzztest"
dir_pw_third_party_googletest = "//third_party/googletest"

# TODO: Seems that re2 support within FuzzTest was deprecated, keeping it defined is giving warning
#Remove if re2 is indeed not needed
# dir_pw_third_party_re2 = "//third_party/re2/src"
}
}
20 changes: 20 additions & 0 deletions docs/guides/BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,26 @@ They pick up environment variables such as `$CFLAGS`, `$CXXFLAGS` and

You likely want `libfuzzer` + `asan` builds instead for local testing.

### `pw_fuzzer` `FuzzTests`

An Alternative way for writing and running Fuzz Tests is Google's `FuzzTest`
framework, integrated through `pw_fuzzer`. The Tests will have to be built and
executed manually.

```
./scripts/build/build_examples.py --target linux-x64-tests-clang-pw-fuzztest build
```

NOTE: `asan` is enabled by default in FuzzTest, so adding to build_examples
target does not make a difference.

Tests will be located in:
`out/linux-x64-tests-clang-pw-fuzztest/chip_pw_fuzztest/tests/` where
`chip_pw_fuzztest` is the name of the toolchain used.

- Details on How To Run Fuzz Tests in
[Running FuzzTests](https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/fuzz_testing.md)

## Build custom configuration

The build is configured by setting build arguments. These you can set in one of
Expand Down
Loading
Loading