From a003b56705fb3f7646f121641f0011f3a48df234 Mon Sep 17 00:00:00 2001 From: AYA Date: Fri, 28 Jun 2024 15:52:26 +0200 Subject: [PATCH 01/17] latest trial to build pw_fuzz --- .gitmodules | 12 ++++ BUILD.gn | 11 +++ build/chip/fuzz_test.gni | 2 + build/config/BUILDCONFIG.gn | 5 ++ build/config/compiler/BUILD.gn | 3 +- build/toolchain/pw_fuzzer/BUILD.gn | 69 +++++++++++++++++++ src/lib/format/tests/BUILD.gn | 31 +++++++++ src/lib/format/tests/FuzzPayloadDecoderPW.cpp | 68 ++++++++++++++++++ third_party/abseil-cpp/src | 1 + third_party/fuzztest | 1 + third_party/googletest | 1 + third_party/re2/src | 1 + 12 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 build/toolchain/pw_fuzzer/BUILD.gn create mode 100644 src/lib/format/tests/FuzzPayloadDecoderPW.cpp create mode 160000 third_party/abseil-cpp/src create mode 160000 third_party/fuzztest create mode 160000 third_party/googletest create mode 160000 third_party/re2/src diff --git a/.gitmodules b/.gitmodules index dee55fa9536e7e..ba78c3f5092205 100644 --- a/.gitmodules +++ b/.gitmodules @@ -325,3 +325,15 @@ url = https://github.com/Infineon/optiga-trust-m.git branch = matter_support 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 diff --git a/BUILD.gn b/BUILD.gn index ddd893a4ca49d8..ecfb08174d8a70 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -61,6 +61,17 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { } } + if (pw_enable_fuzz_test_targets) { + group("pw_fuzz_tests") { + deps = [ + #":pw_module_tests.run($dir_pigweed/targets/host:host_clang_fuzz)", + #"$dir_pigweed:pw_module_tests.run($dir_pigweed/targets/host:host_clang_fuzz)", + # "${chip_root}/build/toolchain/pw_fuzzer:clang_pw_fuzz", + "${chip_root}/src/lib/format/tests:FuzzPayloadDecoderPW(//build/toolchain/pw_fuzzer:clang_pw_fuzz)", + ] + } + } + # Matter's in-tree pw_python_package or pw_python_distribution targets. _matter_python_packages = [ "//examples/chef", diff --git a/build/chip/fuzz_test.gni b/build/chip/fuzz_test.gni index 784ed60273b02a..42d44209a3d9b9 100644 --- a/build/chip/fuzz_test.gni +++ b/build/chip/fuzz_test.gni @@ -20,6 +20,8 @@ import("${chip_root}/build/chip/tests.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. diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn index 7eb09b79c4c22d..e3b3bfabdf73f5 100644 --- a/build/config/BUILDCONFIG.gn +++ b/build/config/BUILDCONFIG.gn @@ -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 == "") { @@ -68,6 +70,9 @@ if (_chip_defaults.custom_toolchain != "") { } else { _default_toolchain = _chip_defaults.custom_toolchain } + # } else if (is_pw_fuzz == true) { + # _default_toolchain = + # "${_build_overrides.build_root}/toolchain/pw_fuzzer:clang_pw_fuzz" } else if (target_os == "all") { _default_toolchain = "${_pigweed_overrides.dir_pw_toolchain}/default" } else if (target_os == "linux") { diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index 1e634152a61d49..0f3a12cee135fb 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn @@ -307,7 +307,8 @@ config("strict_warnings") { config("warnings_default") { configs = [ ":warnings_common", - ":strict_warnings", + + # ":strict_warnings", ":disabled_warnings", ] } diff --git a/build/toolchain/pw_fuzzer/BUILD.gn b/build/toolchain/pw_fuzzer/BUILD.gn new file mode 100644 index 00000000000000..67f378706e4cae --- /dev/null +++ b/build/toolchain/pw_fuzzer/BUILD.gn @@ -0,0 +1,69 @@ +# 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. + +# TODO: maybe delete this file as it seems useless as of 25/06 +import("//build_overrides/build.gni") +import("//build_overrides/pigweed.gni") + +import("${build_root}/toolchain/gcc_toolchain.gni") + +import("$dir_pigweed/targets/host/target_toolchains.gni") + +#import("$dir_pigweed/pw_toolchain/host_clang/toolchains.gni") + +gcc_toolchain("clang_pw_fuzz") { + toolchain_args = { + name = "clang_pw_fuzz" + print("gcc_toolchain(clang_pw_fuzz)") + + # print("pw_toolchain_host_clang = $pw_toolchain_host_clang") + # forward_variables_from(pw_toolchain_host_clang.fuzz, "*", [ "name" ]) + + # print( + # "pw_target_toolchain_host.clang_fuzz = ${pw_target_toolchain_host.clang_fuzz}") + forward_variables_from(pw_target_toolchain_host.clang_fuzz, "*", [ "name" ]) + + #This is needed to have the defaults passed from pw_target_toolchain_host.clang_fuzz, + forward_variables_from(defaults, "*") + + #print("defaults = $defaults") + print("pw_toolchain_FUZZING_ENABLED = ${pw_toolchain_FUZZING_ENABLED}") + 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 + + # pw_toolchain_FUZZING_ENABLED = true + treat_warnings_as_errors = false + + # dir_pw_third_party_abseil_cpp = "$dir_pigweed/third_party/abseil-cpp/src" + # dir_pw_third_party_fuzztest = "$dir_pigweed/third_party/fuzztest" + # dir_pw_third_party_googletest = "$dir_pigweed/third_party/googletest" + # dir_pw_third_party_re2 = "$dir_pigweed/third_party/re2/src" + + #dir_pw_third_party_abseil_cpp = "$dir_pw_third_party/abseil-cpp/" + + 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" + dir_pw_third_party_re2 = "//third_party/re2/src" + + # use below if above dont work + #dir_pw_third_party_abseil_cpp="//third_party/abseil-cpp" + #dir_pw_third_party_re2 = "//third_party/re2" + } +} diff --git a/src/lib/format/tests/BUILD.gn b/src/lib/format/tests/BUILD.gn index 1001a3bd9e77d4..39eb423612e295 100644 --- a/src/lib/format/tests/BUILD.gn +++ b/src/lib/format/tests/BUILD.gn @@ -56,3 +56,34 @@ if (enable_fuzz_test_targets) { ] } } + +if (pw_enable_fuzz_test_targets) { + # DOCSTAG: [pwfuzzer_examples_fuzztest-gn] + pw_test("FuzzPayloadDecoderPW") { + sources = [ "FuzzPayloadDecoderPW.cpp" ] + + # print(" aAAAA: $dir_pw_fuzzer") + deps = [ + #":pw_module_tests.run($dir_pigweed/targets/host:host_clang_fuzz)", + "$dir_pw_fuzzer:fuzztest", # <- Added! + + # "${chip_root}/build/toolchain/pw_fuzzer:clang_pw_fuzz", + + # "${chip_root}:fuzzers", + ] + public_deps = [ + "${chip_root}/src/controller/data_model:cluster-tlv-metadata", + "${chip_root}/src/lib/core", + "${chip_root}/src/lib/format:flat-tree", + "${chip_root}/src/lib/format:protocol-decoder", + "${chip_root}/src/lib/format:protocol-tlv-metadata", + "${chip_root}/src/lib/support", + "${chip_root}/src/platform/logging:stdio", + ] + + # this is necessary so it is not disable in third_party/pigweed/repo/pw_unit_test/test.gni + # otherwise it will be built successfully but with FuzzPayloadDecoderPW.DISABLED.ninja and no executable + + enable_if = true + } +} diff --git a/src/lib/format/tests/FuzzPayloadDecoderPW.cpp b/src/lib/format/tests/FuzzPayloadDecoderPW.cpp new file mode 100644 index 00000000000000..7dfa48c2636195 --- /dev/null +++ b/src/lib/format/tests/FuzzPayloadDecoderPW.cpp @@ -0,0 +1,68 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * All rights reserved. + * + * 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. + */ +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace { + +using namespace chip::Decoders; +using namespace chip::FlatTree; +using namespace chip::TLV; +using namespace chip::TLVMeta; + +void RunDecodePW(const std::vector& bytes) +{ + const uint8_t* const data{bytes.data()}; + const int size{static_cast(bytes.size())}; + + chip::ByteSpan payload(data, size); + + PayloadDecoderInitParams params; + params.SetProtocolDecodeTree(chip::TLVMeta::protocols_meta).SetClusterDecodeTree(chip::TLVMeta::clusters_meta); + + + // Try some SC variants + params.SetProtocol(chip::Protocols::SecureChannel::Id); + params.SetMessageType(0); + + chip::Decoders::PayloadDecoder<64, 128> decoder(params); + + decoder.StartDecoding(payload); + +printf("Test is running"); + + PayloadEntry entry; + while (decoder.Next(entry)) + { + // Nothing to do ... + } +} + + + + +FUZZ_TEST(PayloadDecoder, RunDecodePW); +} \ No newline at end of file diff --git a/third_party/abseil-cpp/src b/third_party/abseil-cpp/src new file mode 160000 index 00000000000000..3ab97e7212bff9 --- /dev/null +++ b/third_party/abseil-cpp/src @@ -0,0 +1 @@ +Subproject commit 3ab97e7212bff931a201c794fa1331960158bbfa diff --git a/third_party/fuzztest b/third_party/fuzztest new file mode 160000 index 00000000000000..6eb010c7223a6a --- /dev/null +++ b/third_party/fuzztest @@ -0,0 +1 @@ +Subproject commit 6eb010c7223a6aa609b94d49bfc06ac88f922961 diff --git a/third_party/googletest b/third_party/googletest new file mode 160000 index 00000000000000..1d17ea141d2c11 --- /dev/null +++ b/third_party/googletest @@ -0,0 +1 @@ +Subproject commit 1d17ea141d2c11b8917d2c7d029f1c4e2b9769b2 diff --git a/third_party/re2/src b/third_party/re2/src new file mode 160000 index 00000000000000..85dd7ad833a730 --- /dev/null +++ b/third_party/re2/src @@ -0,0 +1 @@ +Subproject commit 85dd7ad833a73095ecf3e3baea608ba051bbe2c7 From 6d416dc0a3044a8d3f74c638c19d97e51431dc72 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Sun, 18 Aug 2024 18:06:17 +0200 Subject: [PATCH 02/17] migrating FuzzPayloadDecoder FuzzTest --- build/config/compiler/BUILD.gn | 9 +++- src/lib/format/tests/FuzzPayloadDecoderPW.cpp | 54 +++++++++++++------ 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index 0f3a12cee135fb..779bb4fc4c2337 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn @@ -357,13 +357,20 @@ config("runtime_default") { } if (current_os == "linux" || current_os == "tizen" || current_os == "webos") { libs = [ - "atomic", "dl", "pthread", "rt", ] } + #TODO: see what to do with this workaround + # work around because when building pw_fuzzer, the downstreamed pw_fuzzer toolchain makes us use --sysroot flag in the clang++ invocation; the sysroot flag points to the "clang_sysroot" subdirectory of the CIPD environemnt, + # the clang_sysroot folder is populated from CIPD (fuchsia/third_party/sysroot/linux) which does not include the libatomic library and makes the FuzzTest linking fail + # FYI, using the sysroot flag is only activated when pw_toolchain_OSS_FUZZ_ENABLED=false + if (is_pw_fuzz == false) { + libs += [ "atomic" ] + } + cflags = [] ldflags = [] diff --git a/src/lib/format/tests/FuzzPayloadDecoderPW.cpp b/src/lib/format/tests/FuzzPayloadDecoderPW.cpp index 7dfa48c2636195..3939ef80ca7355 100644 --- a/src/lib/format/tests/FuzzPayloadDecoderPW.cpp +++ b/src/lib/format/tests/FuzzPayloadDecoderPW.cpp @@ -23,8 +23,8 @@ #include #include -#include #include +#include namespace { @@ -32,37 +32,61 @@ using namespace chip::Decoders; using namespace chip::FlatTree; using namespace chip::TLV; using namespace chip::TLVMeta; +using namespace fuzztest; -void RunDecodePW(const std::vector& bytes) +void RunDecodePW(const std::vector & bytes, chip::Protocols::Id mProtocol, uint8_t mMessageType) { - const uint8_t* const data{bytes.data()}; - const int size{static_cast(bytes.size())}; - - chip::ByteSpan payload(data, size); PayloadDecoderInitParams params; params.SetProtocolDecodeTree(chip::TLVMeta::protocols_meta).SetClusterDecodeTree(chip::TLVMeta::clusters_meta); + // Trying Different Protocols + params.SetProtocol(mProtocol); + // Trying different MessageTypes + params.SetMessageType(mMessageType); + chip::Decoders::PayloadDecoder<64, 128> decoder(params); + const uint8_t * const data{ bytes.data() }; + const int size{ static_cast(bytes.size()) }; - // Try some SC variants - params.SetProtocol(chip::Protocols::SecureChannel::Id); - params.SetMessageType(0); - - chip::Decoders::PayloadDecoder<64, 128> decoder(params); + chip::ByteSpan payload(data, size); decoder.StartDecoding(payload); -printf("Test is running"); - PayloadEntry entry; while (decoder.Next(entry)) { // Nothing to do ... } + + // TODO: remove + // PRINT BYTES: To check the combination of bytes being printed + // std::cout << "bytes: "; + // for (const auto& byte : bytes) { + // std::cout << static_cast(byte) << " "; + // } + // std::cout <>(), ProtocolIDs(), Arbitrary()); +// TODO: remove +// this test just to make sure regular unit tests are working +TEST(PayloadDecoder, OnePlustTwoIsTwoPlusOne) +{ + EXPECT_EQ(1 + 2, 2 + 1); +} -FUZZ_TEST(PayloadDecoder, RunDecodePW); -} \ No newline at end of file +} // namespace From 2b9ffe637fc85ff1f62eaf9c2de816cdad16acd4 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Sun, 18 Aug 2024 18:43:08 +0200 Subject: [PATCH 03/17] fix error related to latomic --- build/config/compiler/BUILD.gn | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index 7c26409832412f..a503eda97f9f7c 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn @@ -373,14 +373,14 @@ config("runtime_default") { "pthread", "rt", ] - } - #TODO: see what to do with this workaround - # work around because when building pw_fuzzer, the downstreamed pw_fuzzer toolchain makes us use --sysroot flag in the clang++ invocation; the sysroot flag points to the "clang_sysroot" subdirectory of the CIPD environemnt, - # the clang_sysroot folder is populated from CIPD (fuchsia/third_party/sysroot/linux) which does not include the libatomic library and makes the FuzzTest linking fail - # FYI, using the sysroot flag is only activated when pw_toolchain_OSS_FUZZ_ENABLED=false - if (is_pw_fuzz == false) { - libs += [ "atomic" ] + #TODO: see what to do with this workaround + # work around because when building pw_fuzzer, the downstreamed pw_fuzzer toolchain makes us use --sysroot flag in the clang++ invocation; the sysroot flag points to the "clang_sysroot" subdirectory of the CIPD environemnt, + # the clang_sysroot folder is populated from CIPD (fuchsia/third_party/sysroot/linux) which does not include the libatomic library and makes the FuzzTest linking fail + # FYI, using the sysroot flag is only activated when pw_toolchain_OSS_FUZZ_ENABLED=false + if (is_pw_fuzz == false) { + libs += [ "atomic" ] + } } cflags = [] From 6f7468cfbe7bd31966818169653eda73c57242ab Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Mon, 19 Aug 2024 12:41:05 +0200 Subject: [PATCH 04/17] adding template for pw_fuzz_tests --- BUILD.gn | 2 +- build/chip/fuzz_test.gni | 54 ++++++++++++++++++++++++++++++ build/config/compiler/BUILD.gn | 16 +++------ build/toolchain/pw_fuzzer/BUILD.gn | 9 ++--- src/lib/format/tests/BUILD.gn | 48 +++++++++++++++++--------- 5 files changed, 97 insertions(+), 32 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 04af7d9ae924f4..f781635e05dd93 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -68,7 +68,7 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { #":pw_module_tests.run($dir_pigweed/targets/host:host_clang_fuzz)", #"$dir_pigweed:pw_module_tests.run($dir_pigweed/targets/host:host_clang_fuzz)", # "${chip_root}/build/toolchain/pw_fuzzer:clang_pw_fuzz", - "${chip_root}/src/lib/format/tests:FuzzPayloadDecoderPW(//build/toolchain/pw_fuzzer:clang_pw_fuzz)", + "${chip_root}/src/lib/format/tests:FuzzPayloadDecoderPW(//build/toolchain/pw_fuzzer:chip_pw_fuzztest)", ] } } diff --git a/build/chip/fuzz_test.gni b/build/chip/fuzz_test.gni index 42d44209a3d9b9..878ff07caf4b9f 100644 --- a/build/chip/fuzz_test.gni +++ b/build/chip/fuzz_test.gni @@ -14,8 +14,11 @@ 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 && @@ -68,3 +71,54 @@ template("chip_fuzz_target") { } } } + +template("chip_pw_fuzz_target") { + not_needed([ "target_name" ]) + if (!defined(invoker.sources)) { + sources = [] + } + if (!defined(invoker.public_deps)) { + public_deps = [] + } + if (!defined(invoker.deps)) { + deps = [] + } + + if (defined(invoker.test_sources)) { + foreach(_test, invoker.test_sources) { + _test_name = string_replace(_test, ".cpp", "") + + _test_output_dir = "${root_out_dir}/fuzz_tests" + if (defined(invoker.output_dir)) { + _test_output_dir = invoker.output_dir + } + + pw_test(_test_name) { + forward_variables_from(invoker, + [ + "deps", + "public_deps", + "cflags", + "configs", + ]) + + # TODO see what to do with this, I added it to try running FuzzTest with compatibility mode, since this is the mode supported by OSS-fuzz + # defines = [ + # "FUZZTEST_COMPATIBILITY_MODE=libfuzzer", + # "MAKE_BUILD_TYPE=RelWithDebug", + # ] + + #cflags = [ "-Wno-error=format-nonliteral" ] CAN NOT USE since strict_warning defines the opposite + + sources = [ _test ] + output_dir = _test_output_dir + + deps += [ "$dir_pw_fuzzer:fuzztest" ] + + # this is necessary so it is not disable in third_party/pigweed/repo/pw_unit_test/test.gni + # otherwise it will be built successfully but with FuzzPayloadDecoderPW.DISABLED.ninja and no executable + enable_if = true + } + } + } +} diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index a503eda97f9f7c..5abb3e32467ac5 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn @@ -239,7 +239,9 @@ config("strict_warnings") { "-Wunreachable-code", "-Wvla", "-Wformat", - "-Wformat-nonliteral", + + #TODO: Remove this workaround to build FuzzTests; Abseil C++ submodule is has an error related to this warning + #"-Wformat-nonliteral", "-Wformat-security", ] @@ -308,8 +310,7 @@ config("strict_warnings") { config("warnings_default") { configs = [ ":warnings_common", - - # ":strict_warnings", + ":strict_warnings", ":disabled_warnings", ] } @@ -369,18 +370,11 @@ config("runtime_default") { } if (current_os == "linux" || current_os == "tizen" || current_os == "webos") { libs = [ + "atomic", "dl", "pthread", "rt", ] - - #TODO: see what to do with this workaround - # work around because when building pw_fuzzer, the downstreamed pw_fuzzer toolchain makes us use --sysroot flag in the clang++ invocation; the sysroot flag points to the "clang_sysroot" subdirectory of the CIPD environemnt, - # the clang_sysroot folder is populated from CIPD (fuchsia/third_party/sysroot/linux) which does not include the libatomic library and makes the FuzzTest linking fail - # FYI, using the sysroot flag is only activated when pw_toolchain_OSS_FUZZ_ENABLED=false - if (is_pw_fuzz == false) { - libs += [ "atomic" ] - } } cflags = [] diff --git a/build/toolchain/pw_fuzzer/BUILD.gn b/build/toolchain/pw_fuzzer/BUILD.gn index 67f378706e4cae..6ad410a89d2338 100644 --- a/build/toolchain/pw_fuzzer/BUILD.gn +++ b/build/toolchain/pw_fuzzer/BUILD.gn @@ -22,10 +22,10 @@ import("$dir_pigweed/targets/host/target_toolchains.gni") #import("$dir_pigweed/pw_toolchain/host_clang/toolchains.gni") -gcc_toolchain("clang_pw_fuzz") { +gcc_toolchain("chip_pw_fuzztest") { toolchain_args = { - name = "clang_pw_fuzz" - print("gcc_toolchain(clang_pw_fuzz)") + name = "chip_pw_fuzztest" + print("gcc_toolchain(chip_pw_fuzztest)") # print("pw_toolchain_host_clang = $pw_toolchain_host_clang") # forward_variables_from(pw_toolchain_host_clang.fuzz, "*", [ "name" ]) @@ -37,7 +37,8 @@ gcc_toolchain("clang_pw_fuzz") { #This is needed to have the defaults passed from pw_target_toolchain_host.clang_fuzz, forward_variables_from(defaults, "*") - #print("defaults = $defaults") + print("defaults = $defaults") + print("pw_toolchain_FUZZING_ENABLED = ${pw_toolchain_FUZZING_ENABLED}") pw_unit_test_MAIN = "$dir_pw_fuzzer:fuzztest_main" pw_unit_test_BACKEND = "$dir_pw_fuzzer:gtest" diff --git a/src/lib/format/tests/BUILD.gn b/src/lib/format/tests/BUILD.gn index fc9dea9ae799f0..190f1f38e87823 100644 --- a/src/lib/format/tests/BUILD.gn +++ b/src/lib/format/tests/BUILD.gn @@ -58,20 +58,41 @@ if (enable_fuzz_test_targets) { } } -if (pw_enable_fuzz_test_targets) { - # DOCSTAG: [pwfuzzer_examples_fuzztest-gn] - pw_test("FuzzPayloadDecoderPW") { - sources = [ "FuzzPayloadDecoderPW.cpp" ] +# if (pw_enable_fuzz_test_targets) { +# # DOCSTAG: [pwfuzzer_examples_fuzztest-gn] +# pw_test("FuzzPayloadDecoderPW") { +# sources = [ "FuzzPayloadDecoderPW.cpp" ] - # print(" aAAAA: $dir_pw_fuzzer") - deps = [ - #":pw_module_tests.run($dir_pigweed/targets/host:host_clang_fuzz)", - "$dir_pw_fuzzer:fuzztest", # <- Added! +# # print(" aAAAA: $dir_pw_fuzzer") +# deps = [ +# #":pw_module_tests.run($dir_pigweed/targets/host:host_clang_fuzz)", +# "$dir_pw_fuzzer:fuzztest", # <- Added! - # "${chip_root}/build/toolchain/pw_fuzzer:clang_pw_fuzz", +# # "${chip_root}/build/toolchain/pw_fuzzer:clang_pw_fuzz", + +# # "${chip_root}:fuzzers", +# ] +# public_deps = [ +# "${chip_root}/src/controller/data_model:cluster-tlv-metadata", +# "${chip_root}/src/lib/core", +# "${chip_root}/src/lib/format:flat-tree", +# "${chip_root}/src/lib/format:protocol-decoder", +# "${chip_root}/src/lib/format:protocol-tlv-metadata", +# "${chip_root}/src/lib/support", +# "${chip_root}/src/platform/logging:stdio", +# ] + +# # this is necessary so it is not disable in third_party/pigweed/repo/pw_unit_test/test.gni +# # otherwise it will be built successfully but with FuzzPayloadDecoderPW.DISABLED.ninja and no executable + +# enable_if = true +# } +# } + +if (pw_enable_fuzz_test_targets) { + chip_pw_fuzz_target("FuzzPayloadDecoder") { + test_sources = [ "FuzzPayloadDecoderPW.cpp" ] - # "${chip_root}:fuzzers", - ] public_deps = [ "${chip_root}/src/controller/data_model:cluster-tlv-metadata", "${chip_root}/src/lib/core", @@ -81,10 +102,5 @@ if (pw_enable_fuzz_test_targets) { "${chip_root}/src/lib/support", "${chip_root}/src/platform/logging:stdio", ] - - # this is necessary so it is not disable in third_party/pigweed/repo/pw_unit_test/test.gni - # otherwise it will be built successfully but with FuzzPayloadDecoderPW.DISABLED.ninja and no executable - - enable_if = true } } From a8ede40baed2c41f560b0f03333dc8be28a746a1 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Mon, 19 Aug 2024 19:36:26 +0200 Subject: [PATCH 05/17] fix for linux_sysroot issue --- build/chip/fuzz_test.gni | 9 ++++----- build/toolchain/pw_fuzzer/BUILD.gn | 5 +++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/build/chip/fuzz_test.gni b/build/chip/fuzz_test.gni index 878ff07caf4b9f..8f636dea47eae2 100644 --- a/build/chip/fuzz_test.gni +++ b/build/chip/fuzz_test.gni @@ -102,13 +102,12 @@ template("chip_pw_fuzz_target") { "configs", ]) - # TODO see what to do with this, I added it to try running FuzzTest with compatibility mode, since this is the mode supported by OSS-fuzz + # TODO: remove this + #just a test for running FuzzTest with libfuzzer-compatibility mode, since this is the mode supported by OSS-fuzz # defines = [ - # "FUZZTEST_COMPATIBILITY_MODE=libfuzzer", + # "FUZZTEST_COMPATIBILITY_MODE=libfuzzer", # "MAKE_BUILD_TYPE=RelWithDebug", - # ] - - #cflags = [ "-Wno-error=format-nonliteral" ] CAN NOT USE since strict_warning defines the opposite + # ] sources = [ _test ] output_dir = _test_output_dir diff --git a/build/toolchain/pw_fuzzer/BUILD.gn b/build/toolchain/pw_fuzzer/BUILD.gn index 6ad410a89d2338..f7303e1e8392db 100644 --- a/build/toolchain/pw_fuzzer/BUILD.gn +++ b/build/toolchain/pw_fuzzer/BUILD.gn @@ -48,6 +48,11 @@ gcc_toolchain("chip_pw_fuzztest") { 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 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. + remove_default_configs = [ "$dir_pw_toolchain/host_clang:linux_sysroot" ] + # pw_toolchain_FUZZING_ENABLED = true treat_warnings_as_errors = false From 56f2e34c1cba55dce58581f93887ad96d944b969 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Wed, 28 Aug 2024 15:39:08 +0200 Subject: [PATCH 06/17] adding FuzzTests --- BUILD.gn | 9 ++- src/credentials/tests/BUILD.gn | 10 +++ src/credentials/tests/FuzzChipCertPW.cpp | 63 ++++++++++++++++ src/lib/core/tests/BUILD.gn | 10 +++ src/lib/core/tests/FuzzTlvReaderPW.cpp | 33 +++++++++ src/lib/dnssd/minimal_mdns/tests/BUILD.gn | 10 +++ .../tests/FuzzPacketParsingPW.cpp | 73 +++++++++++++++++++ src/lib/format/tests/BUILD.gn | 36 +-------- src/lib/format/tests/FuzzPayloadDecoderPW.cpp | 21 ++---- src/setup_payload/tests/BUILD.gn | 10 +++ src/setup_payload/tests/FuzzBase38PW.cpp | 72 ++++++++++++++++++ 11 files changed, 295 insertions(+), 52 deletions(-) create mode 100644 src/credentials/tests/FuzzChipCertPW.cpp create mode 100644 src/lib/core/tests/FuzzTlvReaderPW.cpp create mode 100644 src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp create mode 100644 src/setup_payload/tests/FuzzBase38PW.cpp diff --git a/BUILD.gn b/BUILD.gn index f781635e05dd93..7fff950f54a07e 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -65,10 +65,11 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { if (pw_enable_fuzz_test_targets) { group("pw_fuzz_tests") { deps = [ - #":pw_module_tests.run($dir_pigweed/targets/host:host_clang_fuzz)", - #"$dir_pigweed:pw_module_tests.run($dir_pigweed/targets/host:host_clang_fuzz)", - # "${chip_root}/build/toolchain/pw_fuzzer:clang_pw_fuzz", - "${chip_root}/src/lib/format/tests:FuzzPayloadDecoderPW(//build/toolchain/pw_fuzzer:chip_pw_fuzztest)", + "${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)", ] } } diff --git a/src/credentials/tests/BUILD.gn b/src/credentials/tests/BUILD.gn index 393b246ef20ee3..46e1f724349102 100644 --- a/src/credentials/tests/BUILD.gn +++ b/src/credentials/tests/BUILD.gn @@ -84,3 +84,13 @@ if (enable_fuzz_test_targets) { ] } } + +if (pw_enable_fuzz_test_targets) { + chip_pw_fuzz_target("fuzz-chip-cert-pw") { + test_source = [ "FuzzChipCertPW.cpp" ] + public_deps = [ + "${chip_root}/src/credentials", + "${chip_root}/src/platform/logging:default", + ] + } +} diff --git a/src/credentials/tests/FuzzChipCertPW.cpp b/src/credentials/tests/FuzzChipCertPW.cpp new file mode 100644 index 00000000000000..773dde16d3355f --- /dev/null +++ b/src/credentials/tests/FuzzChipCertPW.cpp @@ -0,0 +1,63 @@ +#include +#include + +#include +#include + +#include "credentials/CHIPCert.h" + +using namespace chip; +using namespace chip::Credentials; + +using namespace fuzztest; + +void ChipCertFuzzer(const std::vector & bytes) +{ + + ByteSpan span(bytes.data(), bytes.size()); + + { + NodeId nodeId; + FabricId fabricId; + (void) ExtractFabricIdFromCert(span, &fabricId); + (void) ExtractNodeIdFabricIdFromOpCert(span, &nodeId, &fabricId); + } + + { + CATValues cats; + (void) ExtractCATsFromOpCert(span, cats); + } + + { + Credentials::P256PublicKeySpan key; + (void) ExtractPublicKeyFromChipCert(span, key); + } + + { + chip::System::Clock::Seconds32 rcacNotBefore; + (void) ExtractNotBeforeFromChipCert(span, rcacNotBefore); + } + + { + Credentials::CertificateKeyId skid; + (void) ExtractSKIDFromChipCert(span, skid); + } + + { + ChipDN subjectDN; + (void) ExtractSubjectDNFromChipCert(span, subjectDN); + } + + { + ChipCertificateData certData; + (void) DecodeChipCert(span, certData); + } + + { + uint8_t outCertBuf[kMaxDERCertLength]; + MutableByteSpan outCert(outCertBuf); + (void) ConvertChipCertToX509Cert(span, outCert); + } +} + +FUZZ_TEST(ChipCert, ChipCertFuzzer).WithDomains(Arbitrary>()); diff --git a/src/lib/core/tests/BUILD.gn b/src/lib/core/tests/BUILD.gn index eb17707dec1755..264de2c8aafe6b 100644 --- a/src/lib/core/tests/BUILD.gn +++ b/src/lib/core/tests/BUILD.gn @@ -59,3 +59,13 @@ if (enable_fuzz_test_targets) { ] } } + +if (pw_enable_fuzz_test_targets) { + chip_pw_fuzz_target("fuzz-tlv-reader-pw") { + test_source = [ "FuzzTlvReaderPW.cpp" ] + public_deps = [ + "${chip_root}/src/lib/core", + "${chip_root}/src/platform/logging:default", + ] + } +} diff --git a/src/lib/core/tests/FuzzTlvReaderPW.cpp b/src/lib/core/tests/FuzzTlvReaderPW.cpp new file mode 100644 index 00000000000000..e9dbfafa675c59 --- /dev/null +++ b/src/lib/core/tests/FuzzTlvReaderPW.cpp @@ -0,0 +1,33 @@ + +#include +#include + +#include +#include + +#include "lib/core/TLV.h" +#include "lib/core/TLVUtilities.h" + +using chip::TLV::TLVReader; + + +using namespace fuzztest; + + +static CHIP_ERROR FuzzIterator(const TLVReader & aReader, size_t aDepth, void * aContext) +{ + aReader.GetLength(); + aReader.GetTag(); + aReader.GetType(); + return CHIP_NO_ERROR; +} + +void FuzzTlvRead(const std::vector & bytes) +{ + TLVReader reader; + reader.Init(bytes.data(), bytes.size()); + chip::TLV::Utilities::Iterate(reader, FuzzIterator, nullptr); + +} + +FUZZ_TEST(ChipCert, FuzzTlvRead).WithDomains(Arbitrary>()); diff --git a/src/lib/dnssd/minimal_mdns/tests/BUILD.gn b/src/lib/dnssd/minimal_mdns/tests/BUILD.gn index 47e83650d41acc..457c11ee688b94 100644 --- a/src/lib/dnssd/minimal_mdns/tests/BUILD.gn +++ b/src/lib/dnssd/minimal_mdns/tests/BUILD.gn @@ -53,3 +53,13 @@ if (enable_fuzz_test_targets) { ] } } + +if (pw_enable_fuzz_test_targets) { + chip_pw_fuzz_target("fuzz-minmdns-packet-parsing-pw") { + test_source = [ "FuzzPacketParsingPW.cpp" ] + public_deps = [ + "${chip_root}/src/lib/dnssd/minimal_mdns", + "${chip_root}/src/platform/logging:default", + ] + } +} diff --git a/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp b/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp new file mode 100644 index 00000000000000..e3493d578287cc --- /dev/null +++ b/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp @@ -0,0 +1,73 @@ +#include +#include + + +#include +#include + +#include +#include + +using namespace fuzztest; + + +namespace { + + +using namespace chip; +using namespace mdns::Minimal; + +class FuzzDelegate : public ParserDelegate +{ +public: + FuzzDelegate(const mdns::Minimal::BytesRange & packet) : mPacketRange(packet) {} + virtual ~FuzzDelegate() {} + + void OnHeader(ConstHeaderRef & header) override {} + void OnQuery(const QueryData & data) override {} + void OnResource(ResourceType type, const ResourceData & data) override + { + switch (data.GetType()) + { + case QType::SRV: { + mdns::Minimal::SrvRecord srv; + (void) srv.Parse(data.GetData(), mPacketRange); + break; + } + case QType::A: { + chip::Inet::IPAddress addr; + (void) mdns::Minimal::ParseARecord(data.GetData(), &addr); + break; + } + case QType::AAAA: { + chip::Inet::IPAddress addr; + (void) mdns::Minimal::ParseAAAARecord(data.GetData(), &addr); + break; + } + case QType::PTR: { + mdns::Minimal::SerializedQNameIterator name; + (void) mdns::Minimal::ParsePtrRecord(data.GetData(), mPacketRange, &name); + break; + } + default: + // nothing to do + break; + } + } + +private: + mdns::Minimal::BytesRange mPacketRange; +}; + +} // namespace + +void PacketParserFuzz(const std::vector & bytes) +{ + BytesRange packet(bytes.data(), bytes.data() + bytes.size()); + FuzzDelegate delegate(packet); + + mdns::Minimal::ParsePacket(packet, &delegate); + +} + +FUZZ_TEST(MinimalmDNS, PacketParserFuzz).WithDomains(Arbitrary>()); diff --git a/src/lib/format/tests/BUILD.gn b/src/lib/format/tests/BUILD.gn index 190f1f38e87823..1ce417ea91cdc3 100644 --- a/src/lib/format/tests/BUILD.gn +++ b/src/lib/format/tests/BUILD.gn @@ -58,41 +58,9 @@ if (enable_fuzz_test_targets) { } } -# if (pw_enable_fuzz_test_targets) { -# # DOCSTAG: [pwfuzzer_examples_fuzztest-gn] -# pw_test("FuzzPayloadDecoderPW") { -# sources = [ "FuzzPayloadDecoderPW.cpp" ] - -# # print(" aAAAA: $dir_pw_fuzzer") -# deps = [ -# #":pw_module_tests.run($dir_pigweed/targets/host:host_clang_fuzz)", -# "$dir_pw_fuzzer:fuzztest", # <- Added! - -# # "${chip_root}/build/toolchain/pw_fuzzer:clang_pw_fuzz", - -# # "${chip_root}:fuzzers", -# ] -# public_deps = [ -# "${chip_root}/src/controller/data_model:cluster-tlv-metadata", -# "${chip_root}/src/lib/core", -# "${chip_root}/src/lib/format:flat-tree", -# "${chip_root}/src/lib/format:protocol-decoder", -# "${chip_root}/src/lib/format:protocol-tlv-metadata", -# "${chip_root}/src/lib/support", -# "${chip_root}/src/platform/logging:stdio", -# ] - -# # this is necessary so it is not disable in third_party/pigweed/repo/pw_unit_test/test.gni -# # otherwise it will be built successfully but with FuzzPayloadDecoderPW.DISABLED.ninja and no executable - -# enable_if = true -# } -# } - if (pw_enable_fuzz_test_targets) { - chip_pw_fuzz_target("FuzzPayloadDecoder") { - test_sources = [ "FuzzPayloadDecoderPW.cpp" ] - + chip_pw_fuzz_target("fuzz-payload-decoder-pw") { + test_source = [ "FuzzPayloadDecoderPW.cpp" ] public_deps = [ "${chip_root}/src/controller/data_model:cluster-tlv-metadata", "${chip_root}/src/lib/core", diff --git a/src/lib/format/tests/FuzzPayloadDecoderPW.cpp b/src/lib/format/tests/FuzzPayloadDecoderPW.cpp index 3939ef80ca7355..0b2844f3b5b705 100644 --- a/src/lib/format/tests/FuzzPayloadDecoderPW.cpp +++ b/src/lib/format/tests/FuzzPayloadDecoderPW.cpp @@ -39,16 +39,16 @@ void RunDecodePW(const std::vector & bytes, chip::Protocols::Id mP PayloadDecoderInitParams params; params.SetProtocolDecodeTree(chip::TLVMeta::protocols_meta).SetClusterDecodeTree(chip::TLVMeta::clusters_meta); - // Trying Different Protocols + + // Fuzzing with different Protocols params.SetProtocol(mProtocol); - // Trying different MessageTypes + + + // Fuzzing with different MessageTypes params.SetMessageType(mMessageType); chip::Decoders::PayloadDecoder<64, 128> decoder(params); - const uint8_t * const data{ bytes.data() }; - const int size{ static_cast(bytes.size()) }; - - chip::ByteSpan payload(data, size); + chip::ByteSpan payload(bytes.data(), bytes.size()); decoder.StartDecoding(payload); @@ -73,7 +73,7 @@ void RunDecodePW(const std::vector & bytes, chip::Protocols::Id mP // std::cout << "mMessageType: " << mMessageType << std::endl; } -// This allows us to fuzz test with all combinations of protocols +// This allows us to fuzz test with all the combinations of protocols auto ProtocolIDs() { return ElementOf({ chip::Protocols::SecureChannel::Id, chip::Protocols::InteractionModel::Id, chip::Protocols::BDX::Id, @@ -82,11 +82,4 @@ auto ProtocolIDs() FUZZ_TEST(PayloadDecoder, RunDecodePW).WithDomains(Arbitrary>(), ProtocolIDs(), Arbitrary()); -// TODO: remove -// this test just to make sure regular unit tests are working -TEST(PayloadDecoder, OnePlustTwoIsTwoPlusOne) -{ - EXPECT_EQ(1 + 2, 2 + 1); -} - } // namespace diff --git a/src/setup_payload/tests/BUILD.gn b/src/setup_payload/tests/BUILD.gn index 75cdb8a71b0ad3..fc7bd6fe13c83c 100644 --- a/src/setup_payload/tests/BUILD.gn +++ b/src/setup_payload/tests/BUILD.gn @@ -56,3 +56,13 @@ if (enable_fuzz_test_targets) { ] } } + +if (pw_enable_fuzz_test_targets) { + chip_pw_fuzz_target("fuzz-setup-payload-base38-pw") { + test_source = [ "FuzzBase38PW.cpp" ] + public_deps = [ + "${chip_root}/src/platform/logging:stdio", + "${chip_root}/src/setup_payload", + ] + } +} diff --git a/src/setup_payload/tests/FuzzBase38PW.cpp b/src/setup_payload/tests/FuzzBase38PW.cpp new file mode 100644 index 00000000000000..a2a33ec3f62343 --- /dev/null +++ b/src/setup_payload/tests/FuzzBase38PW.cpp @@ -0,0 +1,72 @@ +#include +#include +#include + + +#include +#include + + +#include +#include + +using namespace fuzztest; +using namespace chip; + + +// The property Function +void Base38DecodeFuzz(const std::vector & bytes) +{ + std::string base38EncodedString(reinterpret_cast(bytes.data()), bytes.size()); + std::vector decodedData; + + // Ignoring return value, because in general the data is garbage and won't decode properly. + // We're just testing that the decoder does not crash on the fuzzer-generated inputs. + chip::base38Decode(base38EncodedString, decodedData); +} + + + + + + +/* The property function for a base38 roundtrip Fuzzer. + * It starts by encoding the fuzzing value passed + * into Base38. The encoded value will then be decoded. + * + * The fuzzer verifies that the decoded value is the same + * as the one in input.*/ +void Base38RoundTripFuzz(const std::vector & bytes) +{ + + size_t outputSizeNeeded = base38EncodedLength(bytes.size()); + const size_t kMaxOutputSize = 512; + + + ASSERT_LT(outputSizeNeeded, kMaxOutputSize); + + ByteSpan span(bytes.data(), bytes.size()); + + char encodedBuf[kMaxOutputSize]; + MutableCharSpan encodedSpan(encodedBuf); + CHIP_ERROR encodingError = base38Encode(span, encodedSpan); + ASSERT_EQ(encodingError, CHIP_NO_ERROR); + + std::string base38EncodedString(encodedSpan.data(), encodedSpan.size()); + + std::vector decodedData; + CHIP_ERROR decodingError = base38Decode(base38EncodedString, decodedData); + + + ASSERT_EQ(decodingError, CHIP_NO_ERROR); + + //Make sure that decoded data is equal to the original fuzzed input; the bytes vector + ASSERT_EQ(decodedData, bytes); + +} + +// The invocation of the FuzzTest +FUZZ_TEST(Base38Decoder, Base38DecodeFuzz).WithDomains(Arbitrary>()); + +//Max size of the vector is defined as 306 since that will give an outputSizeNeeded of 511 which is less than the required kMaxOutputSize +FUZZ_TEST(Base38Decoder, Base38RoundTripFuzz).WithDomains(Arbitrary>().WithMaxSize(306)); From 77162e0584b042f5bd4a170978c92c69f051dff8 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Wed, 28 Aug 2024 15:40:34 +0200 Subject: [PATCH 07/17] fixing warning issue --- build/chip/fuzz_test.gni | 73 ++++++++++++++++++------------ build/config/compiler/BUILD.gn | 4 +- build/toolchain/pw_fuzzer/BUILD.gn | 55 ++++++++++------------ 3 files changed, 70 insertions(+), 62 deletions(-) diff --git a/build/chip/fuzz_test.gni b/build/chip/fuzz_test.gni index 8f636dea47eae2..c12f72dd864675 100644 --- a/build/chip/fuzz_test.gni +++ b/build/chip/fuzz_test.gni @@ -72,8 +72,25 @@ 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") { - not_needed([ "target_name" ]) if (!defined(invoker.sources)) { sources = [] } @@ -84,40 +101,38 @@ template("chip_pw_fuzz_target") { deps = [] } - if (defined(invoker.test_sources)) { - foreach(_test, invoker.test_sources) { - _test_name = string_replace(_test, ".cpp", "") + if (defined(invoker.test_source)) { + _test_output_dir = "${root_out_dir}/tests" - _test_output_dir = "${root_out_dir}/fuzz_tests" - if (defined(invoker.output_dir)) { - _test_output_dir = invoker.output_dir - } + if (defined(invoker.output_dir)) { + _test_output_dir = invoker.output_dir + } - pw_test(_test_name) { - forward_variables_from(invoker, - [ - "deps", - "public_deps", - "cflags", - "configs", - ]) + pw_test(target_name) { + forward_variables_from(invoker, + [ + "deps", + "public_deps", + "cflags", + "configs", + "remove_configs", + ]) - # TODO: remove this - #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", - # ] + # 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 = [ _test ] - output_dir = _test_output_dir + sources = invoker.test_source + output_dir = _test_output_dir - deps += [ "$dir_pw_fuzzer:fuzztest" ] + deps += [ "$dir_pw_fuzzer:fuzztest" ] - # this is necessary so it is not disable in third_party/pigweed/repo/pw_unit_test/test.gni - # otherwise it will be built successfully but with FuzzPayloadDecoderPW.DISABLED.ninja and no executable - enable_if = true - } + # 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 } } } diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index 5abb3e32467ac5..fee0d745f6d136 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn @@ -239,9 +239,7 @@ config("strict_warnings") { "-Wunreachable-code", "-Wvla", "-Wformat", - - #TODO: Remove this workaround to build FuzzTests; Abseil C++ submodule is has an error related to this warning - #"-Wformat-nonliteral", + "-Wformat-nonliteral", "-Wformat-security", ] diff --git a/build/toolchain/pw_fuzzer/BUILD.gn b/build/toolchain/pw_fuzzer/BUILD.gn index f7303e1e8392db..69b5a2e3f20f3e 100644 --- a/build/toolchain/pw_fuzzer/BUILD.gn +++ b/build/toolchain/pw_fuzzer/BUILD.gn @@ -12,34 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -# TODO: maybe delete this file as it seems useless as of 25/06 import("//build_overrides/build.gni") import("//build_overrides/pigweed.gni") -import("${build_root}/toolchain/gcc_toolchain.gni") - import("$dir_pigweed/targets/host/target_toolchains.gni") +import("${build_root}/toolchain/gcc_toolchain.gni") -#import("$dir_pigweed/pw_toolchain/host_clang/toolchains.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") { - toolchain_args = { - name = "chip_pw_fuzztest" - print("gcc_toolchain(chip_pw_fuzztest)") - - # print("pw_toolchain_host_clang = $pw_toolchain_host_clang") - # forward_variables_from(pw_toolchain_host_clang.fuzz, "*", [ "name" ]) + forward_variables_from(pw_target_toolchain_host.clang_fuzz, "*", [ "name" ]) - # print( - # "pw_target_toolchain_host.clang_fuzz = ${pw_target_toolchain_host.clang_fuzz}") - forward_variables_from(pw_target_toolchain_host.clang_fuzz, "*", [ "name" ]) - - #This is needed to have the defaults passed from pw_target_toolchain_host.clang_fuzz, + 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, "*") - print("defaults = $defaults") - - print("pw_toolchain_FUZZING_ENABLED = ${pw_toolchain_FUZZING_ENABLED}") pw_unit_test_MAIN = "$dir_pw_fuzzer:fuzztest_main" pw_unit_test_BACKEND = "$dir_pw_fuzzer:gtest" @@ -49,27 +38,33 @@ gcc_toolchain("chip_pw_fuzztest") { is_clang = true # the upstream pigweed host_clang toolchain defines a default sysroot, which results in build errors - # since it does not include SSL and is supposed to be minimal by design. + # 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" ] - # pw_toolchain_FUZZING_ENABLED = true - treat_warnings_as_errors = false + #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" ] + } - # dir_pw_third_party_abseil_cpp = "$dir_pigweed/third_party/abseil-cpp/src" - # dir_pw_third_party_fuzztest = "$dir_pigweed/third_party/fuzztest" - # dir_pw_third_party_googletest = "$dir_pigweed/third_party/googletest" - # dir_pw_third_party_re2 = "$dir_pigweed/third_party/re2/src" + # 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", + ] - #dir_pw_third_party_abseil_cpp = "$dir_pw_third_party/abseil-cpp/" + # 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" - dir_pw_third_party_re2 = "//third_party/re2/src" - # use below if above dont work - #dir_pw_third_party_abseil_cpp="//third_party/abseil-cpp" - #dir_pw_third_party_re2 = "//third_party/re2" + # 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" } } From 6b333c416ef7eed33a0d47f70909bad061023d4c Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Wed, 28 Aug 2024 15:41:48 +0200 Subject: [PATCH 08/17] adding support to build pw-fuzztests with build_examples.py --- scripts/build/build/targets.py | 4 ++++ scripts/build/builders/host.py | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/scripts/build/build/targets.py b/scripts/build/build/targets.py index 8ab6e2ca4e0d58..8f9e115a86c081 100755 --- a/scripts/build/build/targets.py +++ b/scripts/build/build/targets.py @@ -77,6 +77,8 @@ def BuildHostFakeTarget(): "-clang").ExceptIfRe('-ossfuzz') target.AppendModifier("ossfuzz", fuzzing_type=HostFuzzingType.OSS_FUZZ).OnlyIfRe( "-clang").ExceptIfRe('-libfuzzer') + target.AppendModifier("pw-fuzztest", fuzzing_type=HostFuzzingType.PW_FUZZTEST).OnlyIfRe( + "-clang").ExceptIfRe('-(libfuzzer|ossfuzz)') target.AppendModifier('coverage', use_coverage=True).OnlyIfRe( '-(chip-tool|all-clusters)') target.AppendModifier('dmalloc', use_dmalloc=True) @@ -177,6 +179,8 @@ def BuildHostTarget(): "-clang").ExceptIfRe('-ossfuzz') target.AppendModifier("ossfuzz", fuzzing_type=HostFuzzingType.OSS_FUZZ).OnlyIfRe( "-clang").ExceptIfRe('-libfuzzer') + target.AppendModifier("pw-fuzztest", fuzzing_type=HostFuzzingType.PW_FUZZTEST).OnlyIfRe( + "-clang").ExceptIfRe('-(libfuzzer|ossfuzz)') target.AppendModifier('coverage', use_coverage=True).OnlyIfRe( '-(chip-tool|all-clusters|tests)') target.AppendModifier('dmalloc', use_dmalloc=True) diff --git a/scripts/build/builders/host.py b/scripts/build/builders/host.py index 8c03ac7b133501..713fea1609505e 100644 --- a/scripts/build/builders/host.py +++ b/scripts/build/builders/host.py @@ -42,6 +42,7 @@ class HostFuzzingType(Enum): NONE = auto() LIB_FUZZER = auto() OSS_FUZZ = auto() + PW_FUZZTEST = auto() class HostApp(Enum): @@ -376,6 +377,8 @@ def __init__(self, root, runner, app: HostApp, board=HostBoard.NATIVE, self.extra_gn_options.append('is_libfuzzer=true') elif fuzzing_type == HostFuzzingType.OSS_FUZZ: self.extra_gn_options.append('oss_fuzz=true') + elif fuzzing_type == HostFuzzingType.PW_FUZZTEST: + self.extra_gn_options.append('is_pw_fuzz=true pw_enable_fuzz_test_targets=true') if imgui_ui: self.extra_gn_options.append('chip_examples_enable_imgui_ui=true') @@ -464,6 +467,9 @@ def __init__(self, root, runner, app: HostApp, board=HostBoard.NATIVE, if self.app == HostApp.TESTS and fuzzing_type != HostFuzzingType.NONE: self.build_command = 'fuzz_tests' + if self.app == HostApp.TESTS and fuzzing_type == HostFuzzingType.PW_FUZZTEST: + self.build_command = 'pw_fuzz_tests' + def GnBuildArgs(self): if self.board == HostBoard.NATIVE: return self.extra_gn_options From 507e5ee2ff00ef66af9692a0b719d7739f97e3b5 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Wed, 28 Aug 2024 13:43:01 +0000 Subject: [PATCH 09/17] Restyled by whitespace --- src/lib/format/tests/FuzzPayloadDecoderPW.cpp | 2 +- src/setup_payload/tests/FuzzBase38PW.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/format/tests/FuzzPayloadDecoderPW.cpp b/src/lib/format/tests/FuzzPayloadDecoderPW.cpp index 0b2844f3b5b705..e585a47fabdb2c 100644 --- a/src/lib/format/tests/FuzzPayloadDecoderPW.cpp +++ b/src/lib/format/tests/FuzzPayloadDecoderPW.cpp @@ -73,7 +73,7 @@ void RunDecodePW(const std::vector & bytes, chip::Protocols::Id mP // std::cout << "mMessageType: " << mMessageType << std::endl; } -// This allows us to fuzz test with all the combinations of protocols +// This allows us to fuzz test with all the combinations of protocols auto ProtocolIDs() { return ElementOf({ chip::Protocols::SecureChannel::Id, chip::Protocols::InteractionModel::Id, chip::Protocols::BDX::Id, diff --git a/src/setup_payload/tests/FuzzBase38PW.cpp b/src/setup_payload/tests/FuzzBase38PW.cpp index a2a33ec3f62343..6cc0bc10bd3c5e 100644 --- a/src/setup_payload/tests/FuzzBase38PW.cpp +++ b/src/setup_payload/tests/FuzzBase38PW.cpp @@ -14,7 +14,7 @@ using namespace fuzztest; using namespace chip; -// The property Function +// The property Function void Base38DecodeFuzz(const std::vector & bytes) { std::string base38EncodedString(reinterpret_cast(bytes.data()), bytes.size()); @@ -28,14 +28,14 @@ void Base38DecodeFuzz(const std::vector & bytes) - + /* The property function for a base38 roundtrip Fuzzer. * It starts by encoding the fuzzing value passed * into Base38. The encoded value will then be decoded. - * + * * The fuzzer verifies that the decoded value is the same - * as the one in input.*/ + * as the one in input.*/ void Base38RoundTripFuzz(const std::vector & bytes) { @@ -44,7 +44,7 @@ void Base38RoundTripFuzz(const std::vector & bytes) ASSERT_LT(outputSizeNeeded, kMaxOutputSize); - + ByteSpan span(bytes.data(), bytes.size()); char encodedBuf[kMaxOutputSize]; @@ -60,7 +60,7 @@ void Base38RoundTripFuzz(const std::vector & bytes) ASSERT_EQ(decodingError, CHIP_NO_ERROR); - //Make sure that decoded data is equal to the original fuzzed input; the bytes vector + //Make sure that decoded data is equal to the original fuzzed input; the bytes vector ASSERT_EQ(decodedData, bytes); } From 19aefd539fc1f27568752c3671ff4cd357146419 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Wed, 28 Aug 2024 13:43:02 +0000 Subject: [PATCH 10/17] Restyled by clang-format --- src/lib/core/tests/FuzzTlvReaderPW.cpp | 3 --- .../minimal_mdns/tests/FuzzPacketParsingPW.cpp | 10 +++------- src/lib/format/tests/FuzzPayloadDecoderPW.cpp | 1 - src/setup_payload/tests/FuzzBase38PW.cpp | 16 +++------------- 4 files changed, 6 insertions(+), 24 deletions(-) diff --git a/src/lib/core/tests/FuzzTlvReaderPW.cpp b/src/lib/core/tests/FuzzTlvReaderPW.cpp index e9dbfafa675c59..8566017bdc9b65 100644 --- a/src/lib/core/tests/FuzzTlvReaderPW.cpp +++ b/src/lib/core/tests/FuzzTlvReaderPW.cpp @@ -10,10 +10,8 @@ using chip::TLV::TLVReader; - using namespace fuzztest; - static CHIP_ERROR FuzzIterator(const TLVReader & aReader, size_t aDepth, void * aContext) { aReader.GetLength(); @@ -27,7 +25,6 @@ void FuzzTlvRead(const std::vector & bytes) TLVReader reader; reader.Init(bytes.data(), bytes.size()); chip::TLV::Utilities::Iterate(reader, FuzzIterator, nullptr); - } FUZZ_TEST(ChipCert, FuzzTlvRead).WithDomains(Arbitrary>()); diff --git a/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp b/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp index e3493d578287cc..0c9c5b5cf84bd9 100644 --- a/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp +++ b/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp @@ -1,7 +1,6 @@ #include #include - #include #include @@ -10,10 +9,8 @@ using namespace fuzztest; - namespace { - using namespace chip; using namespace mdns::Minimal; @@ -63,11 +60,10 @@ class FuzzDelegate : public ParserDelegate void PacketParserFuzz(const std::vector & bytes) { - BytesRange packet(bytes.data(), bytes.data() + bytes.size()); - FuzzDelegate delegate(packet); - - mdns::Minimal::ParsePacket(packet, &delegate); + BytesRange packet(bytes.data(), bytes.data() + bytes.size()); + FuzzDelegate delegate(packet); + mdns::Minimal::ParsePacket(packet, &delegate); } FUZZ_TEST(MinimalmDNS, PacketParserFuzz).WithDomains(Arbitrary>()); diff --git a/src/lib/format/tests/FuzzPayloadDecoderPW.cpp b/src/lib/format/tests/FuzzPayloadDecoderPW.cpp index e585a47fabdb2c..6fcb59794855a8 100644 --- a/src/lib/format/tests/FuzzPayloadDecoderPW.cpp +++ b/src/lib/format/tests/FuzzPayloadDecoderPW.cpp @@ -43,7 +43,6 @@ void RunDecodePW(const std::vector & bytes, chip::Protocols::Id mP // Fuzzing with different Protocols params.SetProtocol(mProtocol); - // Fuzzing with different MessageTypes params.SetMessageType(mMessageType); chip::Decoders::PayloadDecoder<64, 128> decoder(params); diff --git a/src/setup_payload/tests/FuzzBase38PW.cpp b/src/setup_payload/tests/FuzzBase38PW.cpp index 6cc0bc10bd3c5e..0111c3add34d64 100644 --- a/src/setup_payload/tests/FuzzBase38PW.cpp +++ b/src/setup_payload/tests/FuzzBase38PW.cpp @@ -2,18 +2,15 @@ #include #include - #include #include - #include #include using namespace fuzztest; using namespace chip; - // The property Function void Base38DecodeFuzz(const std::vector & bytes) { @@ -25,11 +22,6 @@ void Base38DecodeFuzz(const std::vector & bytes) chip::base38Decode(base38EncodedString, decodedData); } - - - - - /* The property function for a base38 roundtrip Fuzzer. * It starts by encoding the fuzzing value passed * into Base38. The encoded value will then be decoded. @@ -42,7 +34,6 @@ void Base38RoundTripFuzz(const std::vector & bytes) size_t outputSizeNeeded = base38EncodedLength(bytes.size()); const size_t kMaxOutputSize = 512; - ASSERT_LT(outputSizeNeeded, kMaxOutputSize); ByteSpan span(bytes.data(), bytes.size()); @@ -57,16 +48,15 @@ void Base38RoundTripFuzz(const std::vector & bytes) std::vector decodedData; CHIP_ERROR decodingError = base38Decode(base38EncodedString, decodedData); - ASSERT_EQ(decodingError, CHIP_NO_ERROR); - //Make sure that decoded data is equal to the original fuzzed input; the bytes vector + // Make sure that decoded data is equal to the original fuzzed input; the bytes vector ASSERT_EQ(decodedData, bytes); - } // The invocation of the FuzzTest FUZZ_TEST(Base38Decoder, Base38DecodeFuzz).WithDomains(Arbitrary>()); -//Max size of the vector is defined as 306 since that will give an outputSizeNeeded of 511 which is less than the required kMaxOutputSize +// Max size of the vector is defined as 306 since that will give an outputSizeNeeded of 511 which is less than the required +// kMaxOutputSize FUZZ_TEST(Base38Decoder, Base38RoundTripFuzz).WithDomains(Arbitrary>().WithMaxSize(306)); From eefee38356ebf7b02b38e32c2443c836939e9761 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Wed, 28 Aug 2024 16:02:50 +0200 Subject: [PATCH 11/17] adding pw_fuzz_tests to default target --- BUILD.gn | 4 ++++ build/config/BUILDCONFIG.gn | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 7fff950f54a07e..4efa25007aa523 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -152,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" ] } diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn index e3b3bfabdf73f5..15332b03b0f9cb 100644 --- a/build/config/BUILDCONFIG.gn +++ b/build/config/BUILDCONFIG.gn @@ -70,9 +70,6 @@ if (_chip_defaults.custom_toolchain != "") { } else { _default_toolchain = _chip_defaults.custom_toolchain } - # } else if (is_pw_fuzz == true) { - # _default_toolchain = - # "${_build_overrides.build_root}/toolchain/pw_fuzzer:clang_pw_fuzz" } else if (target_os == "all") { _default_toolchain = "${_pigweed_overrides.dir_pw_toolchain}/default" } else if (target_os == "linux") { From 64f827b9b6e3bc5ffa1140ef394cc6cded2416b1 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Wed, 28 Aug 2024 18:41:11 +0200 Subject: [PATCH 12/17] fixing build_examples test golden standard --- scripts/build/testdata/all_targets_linux_x64.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build/testdata/all_targets_linux_x64.txt b/scripts/build/testdata/all_targets_linux_x64.txt index d202b8aea3c8fb..9da25a5f50da6e 100644 --- a/scripts/build/testdata/all_targets_linux_x64.txt +++ b/scripts/build/testdata/all_targets_linux_x64.txt @@ -8,8 +8,8 @@ cyw30739-{cyw30739b2_p5_evk_01,cyw30739b2_p5_evk_02,cyw30739b2_p5_evk_03,cyw9307 efr32-{brd2704b,brd4316a,brd4317a,brd4318a,brd4319a,brd4186a,brd4187a,brd2601b,brd4187c,brd4186c,brd2703a,brd4338a}-{window-covering,switch,unit-test,light,lock,thermostat,pump}[-rpc][-with-ota-requestor][-icd][-low-power][-shell][-no-logging][-openthread-mtd][-heap-monitoring][-no-openthread-cli][-show-qr-code][-wifi][-rs9116][-wf200][-siwx917][-ipv4][-additional-data-advertising][-use-ot-lib][-use-ot-coap-lib][-no-version][-skip-rps-generation] esp32-{m5stack,c3devkit,devkitc,qemu}-{all-clusters,all-clusters-minimal,energy-management,ota-provider,ota-requestor,shell,light,lock,bridge,temperature-measurement,ota-requestor,tests}[-rpc][-ipv6only][-tracing] genio-lighting-app -linux-fake-tests[-mbedtls][-boringssl][-asan][-tsan][-ubsan][-libfuzzer][-ossfuzz][-coverage][-dmalloc][-clang] -linux-{x64,arm64}-{rpc-console,all-clusters,all-clusters-minimal,chip-tool,thermostat,java-matter-controller,kotlin-matter-controller,minmdns,light,lock,shell,ota-provider,ota-requestor,simulated-app1,simulated-app2,python-bindings,tv-app,tv-casting-app,bridge,fabric-admin,fabric-bridge,tests,chip-cert,address-resolve-tool,contact-sensor,dishwasher,microwave-oven,refrigerator,rvc,air-purifier,lit-icd,air-quality-sensor,network-manager,energy-management}[-nodeps][-nlfaultinject][-platform-mdns][-minmdns-verbose][-libnl][-same-event-loop][-no-interactive][-ipv6only][-no-ble][-no-wifi][-no-thread][-mbedtls][-boringssl][-asan][-tsan][-ubsan][-libfuzzer][-ossfuzz][-coverage][-dmalloc][-clang][-test][-rpc][-with-ui][-evse-test-event][-enable-dnssd-tests][-disable-dnssd-tests][-chip-casting-simplified][-data-model-check][-data-model-disabled][-data-model-enabled] +linux-fake-tests[-mbedtls][-boringssl][-asan][-tsan][-ubsan][-libfuzzer][-ossfuzz][-pw-fuzztest][-coverage][-dmalloc][-clang] +linux-{x64,arm64}-{rpc-console,all-clusters,all-clusters-minimal,chip-tool,thermostat,java-matter-controller,kotlin-matter-controller,minmdns,light,lock,shell,ota-provider,ota-requestor,simulated-app1,simulated-app2,python-bindings,tv-app,tv-casting-app,bridge,fabric-admin,fabric-bridge,tests,chip-cert,address-resolve-tool,contact-sensor,dishwasher,microwave-oven,refrigerator,rvc,air-purifier,lit-icd,air-quality-sensor,network-manager,energy-management}[-nodeps][-nlfaultinject][-platform-mdns][-minmdns-verbose][-libnl][-same-event-loop][-no-interactive][-ipv6only][-no-ble][-no-wifi][-no-thread][-mbedtls][-boringssl][-asan][-tsan][-ubsan][-libfuzzer][-ossfuzz][-pw-fuzztest][-coverage][-dmalloc][-clang][-test][-rpc][-with-ui][-evse-test-event][-enable-dnssd-tests][-disable-dnssd-tests][-chip-casting-simplified][-data-model-check][-data-model-disabled][-data-model-enabled] linux-x64-efr32-test-runner[-clang] imx-{chip-tool,lighting-app,thermostat,all-clusters-app,all-clusters-minimal-app,ota-provider-app}[-release] infineon-psoc6-{lock,light,all-clusters,all-clusters-minimal}[-ota][-updateimage][-trustm] From 406ad005f2678b94e92b5ac6db8c9b6caad7c759 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Mon, 2 Sep 2024 18:29:18 +0200 Subject: [PATCH 13/17] Adding Fuzzing Targets --- src/credentials/tests/FuzzChipCertPW.cpp | 38 ++++++++++- .../tests/FuzzPacketParsingPW.cpp | 63 ++++++++++++++++++- src/lib/format/tests/FuzzPayloadDecoderPW.cpp | 23 ++----- src/setup_payload/tests/FuzzBase38PW.cpp | 11 ++++ 4 files changed, 115 insertions(+), 20 deletions(-) diff --git a/src/credentials/tests/FuzzChipCertPW.cpp b/src/credentials/tests/FuzzChipCertPW.cpp index 773dde16d3355f..73ac1779c732eb 100644 --- a/src/credentials/tests/FuzzChipCertPW.cpp +++ b/src/credentials/tests/FuzzChipCertPW.cpp @@ -13,7 +13,6 @@ using namespace fuzztest; void ChipCertFuzzer(const std::vector & bytes) { - ByteSpan span(bytes.data(), bytes.size()); { @@ -58,6 +57,43 @@ void ChipCertFuzzer(const std::vector & bytes) MutableByteSpan outCert(outCertBuf); (void) ConvertChipCertToX509Cert(span, outCert); } + + { + // TODO: #34352 To Move this to a Fixture once Errors related to FuzzTest Fixtures are resolved + ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); + ByteSpan span(bytes.data(), bytes.size()); + ValidateChipRCAC(span); + chip::Platform::MemoryShutdown(); + } } FUZZ_TEST(ChipCert, ChipCertFuzzer).WithDomains(Arbitrary>()); + +// The Property function for DecodeChipCertFuzzer, The FUZZ_TEST Macro will call this function. +void DecodeChipCertFuzzer(const std::vector & bytes, BitFlags aDecodeFlag) +{ + ByteSpan span(bytes.data(), bytes.size()); + + // TODO: #34352 To Move this to a Fixture once Errors related to FuzzTest Fixtures are resolved + ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); + + ChipCertificateData certData; + (void) DecodeChipCert(span, certData, aDecodeFlag); + + chip::Platform::MemoryShutdown(); +} + +// This function allows us to fuzz using one of three CertDecodeFlags flags; by using FuzzTests's `ElementOf` API, we define an +// input domain by explicitly enumerating the set of values in it More Info: +// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#elementof-domains-element-of +auto AnyCertDecodeFlag() +{ + + constexpr BitFlags NullDecodeFlag; + constexpr BitFlags GenTBSHashFlag(CertDecodeFlags::kGenerateTBSHash); + constexpr BitFlags TrustAnchorFlag(CertDecodeFlags::kIsTrustAnchor); + + return ElementOf({ NullDecodeFlag, GenTBSHashFlag, TrustAnchorFlag }); +} + +FUZZ_TEST(ChipCert, DecodeChipCertFuzzer).WithDomains(Arbitrary>(), AnyCertDecodeFlag()); diff --git a/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp b/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp index 0c9c5b5cf84bd9..ad7975431db32e 100644 --- a/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp +++ b/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp @@ -8,6 +8,7 @@ #include using namespace fuzztest; +using namespace std; namespace { @@ -66,4 +67,64 @@ void PacketParserFuzz(const std::vector & bytes) mdns::Minimal::ParsePacket(packet, &delegate); } -FUZZ_TEST(MinimalmDNS, PacketParserFuzz).WithDomains(Arbitrary>()); +class TxtRecordAccumulator : public TxtRecordDelegate +{ +public: + using DataType = vector>; + + void OnRecord(const BytesRange & name, const BytesRange & value) override + { + mData.push_back(make_pair(AsString(name), AsString(value))); + } + + DataType & Data() { return mData; } + const DataType & Data() const { return mData; } + +private: + DataType mData; + + static string AsString(const BytesRange & range) + { + return string(reinterpret_cast(range.Start()), reinterpret_cast(range.End())); + } +}; + +void TxtResponderFuzz2(const std::vector & aRecord) +{ + + bool equal_sign_present = false; + auto equal_sign_pos = aRecord.end(); + + // This test is only giving a set of values, it can be gives more + vector prefixedRecord{ static_cast(aRecord.size()) }; + + prefixedRecord.insert(prefixedRecord.end(), aRecord.begin(), aRecord.end()); + + TxtRecordAccumulator accumulator; + + // The Function under Test, Check that the function does not Crash + ParseTxtRecord(BytesRange(prefixedRecord.data(), (&prefixedRecord.back() + 1)), &accumulator); + + for (auto it = aRecord.begin(); it != aRecord.end(); it++) + { + // if this is first `=` found in the fuzzed record + if ('=' == static_cast(*it) && false == equal_sign_present) + { + equal_sign_present = true; + equal_sign_pos = it; + } + } + + // The Fuzzed Input (record) needs to have at least two characters in order for ParseTxtRecord to do something + if (aRecord.size() > 1) + { + if (true == equal_sign_present) + { + std::string input_record_value(equal_sign_pos + 1, aRecord.end()); + EXPECT_EQ(accumulator.Data().at(0).second, input_record_value); + } + } +} + +FUZZ_TEST(MinimalmDNS, TxtResponderFuzz2).WithDomains(Arbitrary>().WithMaxSize(254)); +FUZZ_TEST(MinimalmDNS, PacketParserFuzz).WithDomains(Arbitrary>()); diff --git a/src/lib/format/tests/FuzzPayloadDecoderPW.cpp b/src/lib/format/tests/FuzzPayloadDecoderPW.cpp index 6fcb59794855a8..80e53bf0d94993 100644 --- a/src/lib/format/tests/FuzzPayloadDecoderPW.cpp +++ b/src/lib/format/tests/FuzzPayloadDecoderPW.cpp @@ -34,7 +34,8 @@ using namespace chip::TLV; using namespace chip::TLVMeta; using namespace fuzztest; -void RunDecodePW(const std::vector & bytes, chip::Protocols::Id mProtocol, uint8_t mMessageType) +// The Property Function; The FUZZ_TEST macro will call this function, with the fuzzed input domains +void RunDecodeFuzz(const std::vector & bytes, chip::Protocols::Id mProtocol, uint8_t mMessageType) { PayloadDecoderInitParams params; @@ -56,29 +57,15 @@ void RunDecodePW(const std::vector & bytes, chip::Protocols::Id mP { // Nothing to do ... } - - // TODO: remove - // PRINT BYTES: To check the combination of bytes being printed - // std::cout << "bytes: "; - // for (const auto& byte : bytes) { - // std::cout << static_cast(byte) << " "; - // } - // std::cout <>(), ProtocolIDs(), Arbitrary()); +FUZZ_TEST(PayloadDecoder, RunDecodeFuzz).WithDomains(Arbitrary>(), AnyProtocolID(), Arbitrary()); } // namespace diff --git a/src/setup_payload/tests/FuzzBase38PW.cpp b/src/setup_payload/tests/FuzzBase38PW.cpp index 0111c3add34d64..eb76b4020d2665 100644 --- a/src/setup_payload/tests/FuzzBase38PW.cpp +++ b/src/setup_payload/tests/FuzzBase38PW.cpp @@ -5,6 +5,7 @@ #include #include +#include "setup_payload/QRCodeSetupPayloadParser.h" #include #include @@ -54,9 +55,19 @@ void Base38RoundTripFuzz(const std::vector & bytes) ASSERT_EQ(decodedData, bytes); } +void FuzzQRCodeSetupPayloadParser(const std::string & s) +{ + chip::Platform::MemoryInit(); + + SetupPayload payload; + QRCodeSetupPayloadParser(s).populatePayload(payload); +} + // The invocation of the FuzzTest FUZZ_TEST(Base38Decoder, Base38DecodeFuzz).WithDomains(Arbitrary>()); // Max size of the vector is defined as 306 since that will give an outputSizeNeeded of 511 which is less than the required // kMaxOutputSize FUZZ_TEST(Base38Decoder, Base38RoundTripFuzz).WithDomains(Arbitrary>().WithMaxSize(306)); + +FUZZ_TEST(Base38Decoder, FuzzQRCodeSetupPayloadParser).WithDomains(Arbitrary()); From 561c637614bc136a888b0f5c21d95f3c563f7685 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Tue, 3 Sep 2024 14:32:04 +0200 Subject: [PATCH 14/17] Adding Documentation --- docs/guides/BUILDING.md | 20 +++++ docs/testing/fuzz_testing.md | 157 +++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 docs/testing/fuzz_testing.md diff --git a/docs/guides/BUILDING.md b/docs/guides/BUILDING.md index 7cd660227ed165..e72d7f6d9f707b 100644 --- a/docs/guides/BUILDING.md +++ b/docs/guides/BUILDING.md @@ -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 diff --git a/docs/testing/fuzz_testing.md b/docs/testing/fuzz_testing.md new file mode 100644 index 00000000000000..5e4b56340b510d --- /dev/null +++ b/docs/testing/fuzz_testing.md @@ -0,0 +1,157 @@ +# Fuzz testing + +- Fuzz Testing involves providing random and unexpected data as input to + functions and methods to uncover bugs, security vulnerabilities, or to + determine if the software crashes. +- it is often continuous; the function under test is called iteratively with + thousands of different inputs. +- Fuzz testing is often done with sanitizers enabled; to catch memory errors + and undefined behaviour. +- The most commonly used fuzz testing frameworks for C/C++ are LibFuzzer and + AFL. +- [Google's FuzzTest](https://github.com/google/fuzztest) is a newer framework + that simplifies writing fuzz tests with user-friendly APIs and offers more + control over input generation. It also integrates seamlessly with Google + Test (GTest). + +## `Google's FuzzTest` + +- Google FuzzTest is integrated through Pigweed's + [pw_fuzzer](https://pigweed.dev/pw_fuzzer/concepts.html). + +### Use cases + +1. Finding Undefined Behavior with Sanitizers: + + - Running fuzz tests while checking if a crash or other sanitizer-detected + error occurs, allowing detection of subtle memory issues like buffer + overflows and use-after-free errors. + +2. Find Correctness Bugs using Assertions: + - For example, in Roundtrip Fuzzing, fuzzed input is encoded, decoded, and + then verified to match the original input. An example of this can be found + in src/setup_payload/tests/FuzzBase38PW.cpp. + +- More information can be found in the + [FuzzTest Use Cases](https://github.com/google/fuzztest/blob/main/doc/use-cases.md) + documentation. + +### Writing FuzzTests + +Keywords: Property Function, Input Domain + +- FuzzTests are instantiated through the macro call of `FUZZ_TEST`: + +```cpp +FUZZ_TEST(TLVReader, FuzzTlvReader).WithDomains(fuzztest::Arbitrary>()); +``` + +- The Macro invocation calls the **Property Function**, which is + `FuzzTlvReader` above. + +- The **input domains** define the range and type of inputs that the + **property function** will receive during fuzzing, specified using the + `.WithDomains()` clause. +- In the macro above, FuzzTest will generate a wide range of possible byte + vectors to thoroughly test the `FuzzTlvReader` function. + +#### The Property Function + +```cpp +// The Property Function +void FuzzTlvRead(const std::vector & bytes) +{ + TLVReader reader; + reader.Init(bytes.data(), bytes.size()); + chip::TLV::Utilities::Iterate(reader, FuzzIterator, nullptr); +} +``` + +- The Property Functions must return a `void` +- The function will be run with many different inputs in the same process, + trying to trigger a crash. +- It is possible to include Assertions such as during Round-Trip Fuzzing + +- More Information: + https://github.com/google/fuzztest/blob/main/doc/fuzz-test-macro.md#the-property-function + +#### Input Domains + +- FuzzTest Offers many Input Domains, all of which are part of the + `fuzztest::` namespace. +- All native C++ types can be used through `Arbitrary()`: + +```cpp +FUZZ_TEST(Base38Decoder, FuzzQRCodeSetupPayloadParser).WithDomains(Arbitrary()); +``` + +- using vector domains is one of the most common. It is possible to limit the + size of the input vectors (or any container domain) using `.WithMaxSize()` + or `.WithMinSize()`, as shown below: + +```cpp +FUZZ_TEST(MinimalmDNS, TxtResponderFuzz).WithDomains(Arbitrary>().WithMaxSize(254)); +``` + +- `ElementOf` is particularly useful as it allows us to define a domain by + explicitly enumerating the set of values in it and passing it to FuzzTest + invocation. Example: + +```cpp +AnyProtocolID() +{ + return ElementOf({ chip::Protocols::SecureChannel::Id, chip::Protocols::InteractionModel::Id, chip::Protocols::BDX::Id, + chip::Protocols::UserDirectedCommissioning::Id }); +} + +FUZZ_TEST(PayloadDecoder, RunDecodeFuzz).WithDomains(Arbitrary>(), AnyProtocolID(), Arbitrary()); +``` + +- A detailed reference for input domains can be found here: + [FuzzTest Domain Reference](https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#elementof-domains-element-of). + +### Running FuzzTests + +There are several ways to run the tests: + +1. Unit-test mode (where the inputs are only fuzzed for a second): + +```bash +./fuzz-chip-cert-pw +``` + +2. Continuous fuzzing mode; we need to first list the tests, then specify the + FuzzTestCase to run: + +```bash +$ ./fuzz-chip-cert-pw --list_fuzz_tests +[.] Sanitizer coverage enabled. Counter map size: 11134, Cmp map size: 262144 +[*] Fuzz test: ChipCert.ChipCertFuzzer +[*] Fuzz test: ChipCert.DecodeChipCertFuzzer + +$ ./fuzz-chip-cert-pw --fuzz=ChipCert.DecodeChipCertFuzzer +``` + +3. Running all Tests in a TestSuite for a specific time, e.g for 10 minutes + +```bash +#both Fuzz Tests will be run for 10 minutes each +./fuzz-chip-cert-pw --fuzz_for=10m +``` + +4. For Help + +```bash +# FuzzTest related help +./fuzz-chip-cert-pw --helpfull + +# gtest related help +./fuzz-chip-cert-pw --help + +``` + +#### TO ADD: + +- More Information on Test Fixtures (After issues are resolved) +- How to add FuzzTests to the Build System +- More Information on OSS-FUZZ From 3f86877caa11c7427af38ae30f121f60aacac6a9 Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Tue, 3 Sep 2024 14:32:38 +0200 Subject: [PATCH 15/17] cleaning-up tests --- src/credentials/tests/FuzzChipCertPW.cpp | 14 +++++--------- src/lib/core/tests/FuzzTlvReaderPW.cpp | 9 +++++++-- .../minimal_mdns/tests/FuzzPacketParsingPW.cpp | 16 +++++++++------- src/lib/format/tests/FuzzPayloadDecoderPW.cpp | 4 +++- src/setup_payload/tests/FuzzBase38PW.cpp | 18 +++++++++++------- 5 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/credentials/tests/FuzzChipCertPW.cpp b/src/credentials/tests/FuzzChipCertPW.cpp index 73ac1779c732eb..c50415935d3f46 100644 --- a/src/credentials/tests/FuzzChipCertPW.cpp +++ b/src/credentials/tests/FuzzChipCertPW.cpp @@ -6,6 +6,8 @@ #include "credentials/CHIPCert.h" +namespace { + using namespace chip; using namespace chip::Credentials; @@ -47,11 +49,6 @@ void ChipCertFuzzer(const std::vector & bytes) (void) ExtractSubjectDNFromChipCert(span, subjectDN); } - { - ChipCertificateData certData; - (void) DecodeChipCert(span, certData); - } - { uint8_t outCertBuf[kMaxDERCertLength]; MutableByteSpan outCert(outCertBuf); @@ -61,13 +58,12 @@ void ChipCertFuzzer(const std::vector & bytes) { // TODO: #34352 To Move this to a Fixture once Errors related to FuzzTest Fixtures are resolved ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); - ByteSpan span(bytes.data(), bytes.size()); ValidateChipRCAC(span); chip::Platform::MemoryShutdown(); } } -FUZZ_TEST(ChipCert, ChipCertFuzzer).WithDomains(Arbitrary>()); +FUZZ_TEST(FuzzChipCert, ChipCertFuzzer).WithDomains(Arbitrary>()); // The Property function for DecodeChipCertFuzzer, The FUZZ_TEST Macro will call this function. void DecodeChipCertFuzzer(const std::vector & bytes, BitFlags aDecodeFlag) @@ -88,7 +84,6 @@ void DecodeChipCertFuzzer(const std::vector & bytes, BitFlags NullDecodeFlag; constexpr BitFlags GenTBSHashFlag(CertDecodeFlags::kGenerateTBSHash); constexpr BitFlags TrustAnchorFlag(CertDecodeFlags::kIsTrustAnchor); @@ -96,4 +91,5 @@ auto AnyCertDecodeFlag() return ElementOf({ NullDecodeFlag, GenTBSHashFlag, TrustAnchorFlag }); } -FUZZ_TEST(ChipCert, DecodeChipCertFuzzer).WithDomains(Arbitrary>(), AnyCertDecodeFlag()); +FUZZ_TEST(FuzzChipCert, DecodeChipCertFuzzer).WithDomains(Arbitrary>(), AnyCertDecodeFlag()); +} // namespace diff --git a/src/lib/core/tests/FuzzTlvReaderPW.cpp b/src/lib/core/tests/FuzzTlvReaderPW.cpp index 8566017bdc9b65..4296cac3cffb02 100644 --- a/src/lib/core/tests/FuzzTlvReaderPW.cpp +++ b/src/lib/core/tests/FuzzTlvReaderPW.cpp @@ -8,6 +8,8 @@ #include "lib/core/TLV.h" #include "lib/core/TLVUtilities.h" +namespace { + using chip::TLV::TLVReader; using namespace fuzztest; @@ -20,11 +22,14 @@ static CHIP_ERROR FuzzIterator(const TLVReader & aReader, size_t aDepth, void * return CHIP_NO_ERROR; } -void FuzzTlvRead(const std::vector & bytes) +// The Property Function +void FuzzTlvReader(const std::vector & bytes) { TLVReader reader; reader.Init(bytes.data(), bytes.size()); chip::TLV::Utilities::Iterate(reader, FuzzIterator, nullptr); } +// Fuzz tests are instantiated with the FUZZ_TEST macro +FUZZ_TEST(TLVReader, FuzzTlvReader).WithDomains(Arbitrary>()); -FUZZ_TEST(ChipCert, FuzzTlvRead).WithDomains(Arbitrary>()); +} // namespace diff --git a/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp b/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp index ad7975431db32e..aec550b26e026a 100644 --- a/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp +++ b/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsingPW.cpp @@ -7,11 +7,11 @@ #include #include +namespace { + using namespace fuzztest; using namespace std; -namespace { - using namespace chip; using namespace mdns::Minimal; @@ -57,8 +57,6 @@ class FuzzDelegate : public ParserDelegate mdns::Minimal::BytesRange mPacketRange; }; -} // namespace - void PacketParserFuzz(const std::vector & bytes) { BytesRange packet(bytes.data(), bytes.data() + bytes.size()); @@ -67,6 +65,8 @@ void PacketParserFuzz(const std::vector & bytes) mdns::Minimal::ParsePacket(packet, &delegate); } +FUZZ_TEST(MinimalmDNS, PacketParserFuzz).WithDomains(Arbitrary>()); + class TxtRecordAccumulator : public TxtRecordDelegate { public: @@ -89,7 +89,8 @@ class TxtRecordAccumulator : public TxtRecordDelegate } }; -void TxtResponderFuzz2(const std::vector & aRecord) +// The Property Function +void TxtResponderFuzz(const std::vector & aRecord) { bool equal_sign_present = false; @@ -126,5 +127,6 @@ void TxtResponderFuzz2(const std::vector & aRecord) } } -FUZZ_TEST(MinimalmDNS, TxtResponderFuzz2).WithDomains(Arbitrary>().WithMaxSize(254)); -FUZZ_TEST(MinimalmDNS, PacketParserFuzz).WithDomains(Arbitrary>()); +FUZZ_TEST(MinimalmDNS, TxtResponderFuzz).WithDomains(Arbitrary>().WithMaxSize(254)); + +} // namespace diff --git a/src/lib/format/tests/FuzzPayloadDecoderPW.cpp b/src/lib/format/tests/FuzzPayloadDecoderPW.cpp index 80e53bf0d94993..52b51b308d0c2a 100644 --- a/src/lib/format/tests/FuzzPayloadDecoderPW.cpp +++ b/src/lib/format/tests/FuzzPayloadDecoderPW.cpp @@ -59,7 +59,9 @@ void RunDecodeFuzz(const std::vector & bytes, chip::Protocols::Id } } -// This allows us to create a FuzzTest "Input Domain" to fuzz test with all the combinations of protocols. +// This function allows us to fuzz using one of four protocols; by using FuzzTests's `ElementOf` API, we define an +// input domain by explicitly enumerating the set of values in it More Info: +// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#elementof-domains-element-of auto AnyProtocolID() { return ElementOf({ chip::Protocols::SecureChannel::Id, chip::Protocols::InteractionModel::Id, chip::Protocols::BDX::Id, diff --git a/src/setup_payload/tests/FuzzBase38PW.cpp b/src/setup_payload/tests/FuzzBase38PW.cpp index eb76b4020d2665..b39db0905dfea0 100644 --- a/src/setup_payload/tests/FuzzBase38PW.cpp +++ b/src/setup_payload/tests/FuzzBase38PW.cpp @@ -9,6 +9,8 @@ #include #include +namespace { + using namespace fuzztest; using namespace chip; @@ -23,6 +25,9 @@ void Base38DecodeFuzz(const std::vector & bytes) chip::base38Decode(base38EncodedString, decodedData); } +// The invocation of the FuzzTest +FUZZ_TEST(Base38Decoder, Base38DecodeFuzz).WithDomains(Arbitrary>()); + /* The property function for a base38 roundtrip Fuzzer. * It starts by encoding the fuzzing value passed * into Base38. The encoded value will then be decoded. @@ -55,6 +60,10 @@ void Base38RoundTripFuzz(const std::vector & bytes) ASSERT_EQ(decodedData, bytes); } +// Max size of the vector is defined as 306 since that will give an outputSizeNeeded of 511 which is less than the required +// kMaxOutputSize +FUZZ_TEST(Base38Decoder, Base38RoundTripFuzz).WithDomains(Arbitrary>().WithMaxSize(306)); + void FuzzQRCodeSetupPayloadParser(const std::string & s) { chip::Platform::MemoryInit(); @@ -63,11 +72,6 @@ void FuzzQRCodeSetupPayloadParser(const std::string & s) QRCodeSetupPayloadParser(s).populatePayload(payload); } -// The invocation of the FuzzTest -FUZZ_TEST(Base38Decoder, Base38DecodeFuzz).WithDomains(Arbitrary>()); - -// Max size of the vector is defined as 306 since that will give an outputSizeNeeded of 511 which is less than the required -// kMaxOutputSize -FUZZ_TEST(Base38Decoder, Base38RoundTripFuzz).WithDomains(Arbitrary>().WithMaxSize(306)); - FUZZ_TEST(Base38Decoder, FuzzQRCodeSetupPayloadParser).WithDomains(Arbitrary()); + +} // namespace From c7ce9074b216d1f667e34e8dcff3a74765dce33e Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Tue, 3 Sep 2024 14:43:33 +0200 Subject: [PATCH 16/17] spelling mistakes --- .github/.wordlist.txt | 3 +++ docs/testing/fuzz_testing.md | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/.wordlist.txt b/.github/.wordlist.txt index 153345827ee086..4444d8299d26cb 100644 --- a/.github/.wordlist.txt +++ b/.github/.wordlist.txt @@ -37,6 +37,7 @@ AdvSendAdvert AE aef AES +AFL AIDL algs alloc @@ -570,6 +571,7 @@ fsync ftd fullclean fuzzer +fuzztest FW gbl gcloud @@ -1008,6 +1010,7 @@ optionOverride optionsMask optionsOverride orgs +OSS OTA OTADownloader otaDownloadPath diff --git a/docs/testing/fuzz_testing.md b/docs/testing/fuzz_testing.md index 5e4b56340b510d..8e06b7740cf0ec 100644 --- a/docs/testing/fuzz_testing.md +++ b/docs/testing/fuzz_testing.md @@ -3,10 +3,10 @@ - Fuzz Testing involves providing random and unexpected data as input to functions and methods to uncover bugs, security vulnerabilities, or to determine if the software crashes. -- it is often continuous; the function under test is called iteratively with +- it is often continuous; the function under test is called in iteration with thousands of different inputs. - Fuzz testing is often done with sanitizers enabled; to catch memory errors - and undefined behaviour. + and undefined behavior. - The most commonly used fuzz testing frameworks for C/C++ are LibFuzzer and AFL. - [Google's FuzzTest](https://github.com/google/fuzztest) is a newer framework @@ -16,7 +16,7 @@ ## `Google's FuzzTest` -- Google FuzzTest is integrated through Pigweed's +- Google FuzzTest is integrated through Pigweed [pw_fuzzer](https://pigweed.dev/pw_fuzzer/concepts.html). ### Use cases @@ -28,7 +28,7 @@ overflows and use-after-free errors. 2. Find Correctness Bugs using Assertions: - - For example, in Roundtrip Fuzzing, fuzzed input is encoded, decoded, and + - For example, in Round trip Fuzzing, fuzzed input is encoded, decoded, and then verified to match the original input. An example of this can be found in src/setup_payload/tests/FuzzBase38PW.cpp. From 1cfbada19714a43ed2491dfc1180b3c49b20624e Mon Sep 17 00:00:00 2001 From: Alami-Amine Date: Tue, 3 Sep 2024 19:12:40 +0200 Subject: [PATCH 17/17] integrating comments --- .gitmodules | 4 ++++ build/chip/fuzz_test.gni | 19 ++++--------------- build/config/BUILDCONFIG.gn | 2 -- build/toolchain/pw_fuzzer/BUILD.gn | 10 +++++----- docs/guides/BUILDING.md | 4 ++-- docs/testing/fuzz_testing.md | 2 +- scripts/build/build/targets.py | 4 ++-- scripts/build/builders/host.py | 2 +- src/credentials/tests/FuzzChipCertPW.cpp | 2 +- 9 files changed, 20 insertions(+), 29 deletions(-) diff --git a/.gitmodules b/.gitmodules index 3b517968055a4e..40801ec9742517 100644 --- a/.gitmodules +++ b/.gitmodules @@ -332,12 +332,16 @@ [submodule "third_party/abseil-cpp/src"] path = third_party/abseil-cpp/src url = https://github.com/abseil/abseil-cpp.git + platforms = linux,darwin [submodule "third_party/fuzztest"] path = third_party/fuzztest url = https://github.com/google/fuzztest.git + platforms = linux,darwin [submodule "third_party/googletest"] path = third_party/googletest url = https://github.com/google/googletest + platforms = linux,darwin [submodule "third_party/re2/src"] path = third_party/re2/src url = https://github.com/google/re2.git + platforms = linux,darwin diff --git a/build/chip/fuzz_test.gni b/build/chip/fuzz_test.gni index c12f72dd864675..98def1dab0463d 100644 --- a/build/chip/fuzz_test.gni +++ b/build/chip/fuzz_test.gni @@ -72,10 +72,9 @@ template("chip_fuzz_target") { } } -# Define a fuzz target for chip using pw_fuzzer and Google FuzzTest Framework. +# Define a fuzz target for Matter using pw_fuzzer and Google FuzzTest Framework. # -# Fuzz generally only apply on the following environments: -# - linux and mac host builds when using clang +# Google FuzzTest is only supported on Linux and MacOS using Clang: # # Sample usage # @@ -91,16 +90,6 @@ template("chip_fuzz_target") { # # 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" @@ -118,7 +107,7 @@ template("chip_pw_fuzz_target") { "remove_configs", ]) - # TODO: remove this after pw_fuzzer implementation its integration with OSS-Fuzz is complete + # TODO: remove this after pw_fuzzer's 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", @@ -128,7 +117,7 @@ template("chip_pw_fuzz_target") { sources = invoker.test_source output_dir = _test_output_dir - deps += [ "$dir_pw_fuzzer:fuzztest" ] + 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. diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn index 15332b03b0f9cb..7eb09b79c4c22d 100644 --- a/build/config/BUILDCONFIG.gn +++ b/build/config/BUILDCONFIG.gn @@ -47,8 +47,6 @@ _chip_defaults = { declare_args() { # Toolchain to use for host. This is usually set by default. host_toolchain = "" - - is_pw_fuzz = false } if (host_toolchain == "") { diff --git a/build/toolchain/pw_fuzzer/BUILD.gn b/build/toolchain/pw_fuzzer/BUILD.gn index 69b5a2e3f20f3e..385e57cc81be39 100644 --- a/build/toolchain/pw_fuzzer/BUILD.gn +++ b/build/toolchain/pw_fuzzer/BUILD.gn @@ -26,13 +26,13 @@ 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 + # 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 + # The next three lines are needed by the gcc_toolchain template current_os = host_os current_cpu = host_cpu is_clang = true @@ -43,7 +43,7 @@ gcc_toolchain("chip_pw_fuzztest") { # 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. + # 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" ] @@ -63,8 +63,8 @@ gcc_toolchain("chip_pw_fuzztest") { 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 + # TODO: Seems that re2 support within FuzzTest was deprecated, keeping it defined is triggering warning + # Remove if re2 is indeed not needed # dir_pw_third_party_re2 = "//third_party/re2/src" } } diff --git a/docs/guides/BUILDING.md b/docs/guides/BUILDING.md index e72d7f6d9f707b..941b5328887e31 100644 --- a/docs/guides/BUILDING.md +++ b/docs/guides/BUILDING.md @@ -392,8 +392,8 @@ 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. +NOTE: `asan` is enabled by default in FuzzTest, so please do not add it in +build_examples.py invocation. Tests will be located in: `out/linux-x64-tests-clang-pw-fuzztest/chip_pw_fuzztest/tests/` where diff --git a/docs/testing/fuzz_testing.md b/docs/testing/fuzz_testing.md index 8e06b7740cf0ec..b7faeae463a10e 100644 --- a/docs/testing/fuzz_testing.md +++ b/docs/testing/fuzz_testing.md @@ -98,7 +98,7 @@ FUZZ_TEST(MinimalmDNS, TxtResponderFuzz).WithDomains(Arbitrary>( invocation. Example: ```cpp -AnyProtocolID() +auto AnyProtocolID() { return ElementOf({ chip::Protocols::SecureChannel::Id, chip::Protocols::InteractionModel::Id, chip::Protocols::BDX::Id, chip::Protocols::UserDirectedCommissioning::Id }); diff --git a/scripts/build/build/targets.py b/scripts/build/build/targets.py index 8a720affa86364..4e657778d964ee 100755 --- a/scripts/build/build/targets.py +++ b/scripts/build/build/targets.py @@ -78,7 +78,7 @@ def BuildHostFakeTarget(): target.AppendModifier("ossfuzz", fuzzing_type=HostFuzzingType.OSS_FUZZ).OnlyIfRe( "-clang").ExceptIfRe('-libfuzzer') target.AppendModifier("pw-fuzztest", fuzzing_type=HostFuzzingType.PW_FUZZTEST).OnlyIfRe( - "-clang").ExceptIfRe('-(libfuzzer|ossfuzz)') + "-clang").ExceptIfRe('-(libfuzzer|ossfuzz|asan)') target.AppendModifier('coverage', use_coverage=True).OnlyIfRe( '-(chip-tool|all-clusters)') target.AppendModifier('dmalloc', use_dmalloc=True) @@ -181,7 +181,7 @@ def BuildHostTarget(): target.AppendModifier("ossfuzz", fuzzing_type=HostFuzzingType.OSS_FUZZ).OnlyIfRe( "-clang").ExceptIfRe('-libfuzzer') target.AppendModifier("pw-fuzztest", fuzzing_type=HostFuzzingType.PW_FUZZTEST).OnlyIfRe( - "-clang").ExceptIfRe('-(libfuzzer|ossfuzz)') + "-clang").ExceptIfRe('-(libfuzzer|ossfuzz|asan)') target.AppendModifier('coverage', use_coverage=True).OnlyIfRe( '-(chip-tool|all-clusters|tests)') target.AppendModifier('dmalloc', use_dmalloc=True) diff --git a/scripts/build/builders/host.py b/scripts/build/builders/host.py index a2ec62b34d4d89..b69421a782e948 100644 --- a/scripts/build/builders/host.py +++ b/scripts/build/builders/host.py @@ -381,7 +381,7 @@ def __init__(self, root, runner, app: HostApp, board=HostBoard.NATIVE, elif fuzzing_type == HostFuzzingType.OSS_FUZZ: self.extra_gn_options.append('oss_fuzz=true') elif fuzzing_type == HostFuzzingType.PW_FUZZTEST: - self.extra_gn_options.append('is_pw_fuzz=true pw_enable_fuzz_test_targets=true') + self.extra_gn_options.append('pw_enable_fuzz_test_targets=true') if imgui_ui: self.extra_gn_options.append('chip_examples_enable_imgui_ui=true') diff --git a/src/credentials/tests/FuzzChipCertPW.cpp b/src/credentials/tests/FuzzChipCertPW.cpp index c50415935d3f46..bb929b392500d7 100644 --- a/src/credentials/tests/FuzzChipCertPW.cpp +++ b/src/credentials/tests/FuzzChipCertPW.cpp @@ -56,7 +56,7 @@ void ChipCertFuzzer(const std::vector & bytes) } { - // TODO: #34352 To Move this to a Fixture once Errors related to FuzzTest Fixtures are resolved + // TODO: #35369 Move this to a Fixture once Errors related to FuzzTest Fixtures are resolved ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); ValidateChipRCAC(span); chip::Platform::MemoryShutdown();