From 19132a37af8583e1df5f470c6e7e93795161602d Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Tue, 5 Jul 2022 15:39:26 -0400 Subject: [PATCH] Add several fuzz targets built when linux/mac are built with clang (#20285) * Add a fuzzing target for the tlv reader * Restyle * Also enable asan for this fuzzing library * Add clang tests to targets, add build default to main build file * Restyle * Fuzzert for some chip credentials calls * A few more fuzzing calls on various things taking a bytespan * Restyle * Add a fuzz target for minmdns packet parsing * update build target after new test clang was added as a build target * Reorganize fuzz target rules into a separate file, so that build logic is shared * Restyle --- BUILD.gn | 17 +++++ build/chip/fuzz_test.gni | 64 ++++++++++++++++++ scripts/build/build/targets.py | 1 + scripts/build/testdata/build_linux_on_x64.txt | 12 ++++ src/credentials/tests/BUILD.gn | 8 +++ src/credentials/tests/FuzzChipCert.cpp | 36 ++++++++++ src/lib/core/tests/BUILD.gn | 8 +++ src/lib/core/tests/FuzzTlvReader.cpp | 21 ++++++ src/lib/dnssd/minimal_mdns/tests/BUILD.gn | 8 +++ .../minimal_mdns/tests/FuzzPacketParsing.cpp | 65 +++++++++++++++++++ 10 files changed, 240 insertions(+) create mode 100644 build/chip/fuzz_test.gni create mode 100644 src/credentials/tests/FuzzChipCert.cpp create mode 100644 src/lib/core/tests/FuzzTlvReader.cpp create mode 100644 src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsing.cpp diff --git a/BUILD.gn b/BUILD.gn index e581759f25e85d..b54672a8124a27 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -27,9 +27,12 @@ import("$dir_pw_build/python.gni") # This build file should not be used in superproject builds. assert(chip_root == "//") +import("${chip_root}/build/chip/fuzz_test.gni") import("${chip_root}/build/chip/tests.gni") import("${chip_root}/build/chip/tools.gni") +import("${build_root}/config/compiler/compiler.gni") + import("//src/crypto/crypto.gni") if (current_toolchain != "${dir_pw_toolchain}/default:default") { @@ -40,6 +43,16 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { enable_pylib = false } + if (enable_fuzz_test_targets) { + group("fuzz_tests") { + deps = [ + "${chip_root}/src/credentials/tests:fuzz-chip-cert", + "${chip_root}/src/lib/core/tests:fuzz-tlv-reader", + "${chip_root}/src/lib/dnssd/minimal_mdns/tests:fuzz-minmdns-packet-parsing", + ] + } + } + # Python packages for supporting specific targets. pw_python_group("python_packages") { python_deps = [ @@ -88,6 +101,10 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") { "${nlunit_test_root}:nlunit-test", ] + if (enable_fuzz_test_targets) { + deps += [ "//:fuzz_tests" ] + } + if (chip_device_platform != "none") { deps += [ "${chip_root}/src/app/server" ] } diff --git a/build/chip/fuzz_test.gni b/build/chip/fuzz_test.gni new file mode 100644 index 00000000000000..3d101c21e03762 --- /dev/null +++ b/build/chip/fuzz_test.gni @@ -0,0 +1,64 @@ +# Copyright (c) 2020 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") +import("${build_root}/config/compiler/compiler.gni") + +declare_args() { + enable_fuzz_test_targets = + is_clang && (current_os == "linux" || current_os == "mac") +} + +# Define a fuzz target for chip. +# +# Fuzz generally only apply on the following environments: +# - linux and mac host builds when using clang +# +# Sample usage +# +# chip_fuzz_target("fuzz-target-name") { +# sources = [ +# "FuzzTarget.cpp", # Fuzz target +# ] +# +# public_deps = [ +# "${chip_root}/src/lib/foo", # add dependencies here +# "${nlunit_test_root}:nlunit-test", +# ] +# } +# +# +template("chip_fuzz_target") { + if (enable_fuzz_test_targets) { + executable(target_name) { + forward_variables_from(invoker, "*") + + if (defined(public_configs)) { + public_configs += [ + "//build/config/compiler:libfuzzer_fuzzing", + "//build/config/compiler:sanitize_address", + ] + } else { + public_configs = [ + "//build/config/compiler:libfuzzer_fuzzing", + "//build/config/compiler:sanitize_address", + ] + } + if (!defined(oubput_dir)) { + output_dir = "${root_out_dir}/tests" + } + } + } +} diff --git a/scripts/build/build/targets.py b/scripts/build/build/targets.py index 04486da645a944..1c7e4789223f59 100644 --- a/scripts/build/build/targets.py +++ b/scripts/build/build/targets.py @@ -308,6 +308,7 @@ def HostTargets(): test_target = Target(HostBoard.NATIVE.PlatformName(), HostBuilder) for board in [HostBoard.NATIVE, HostBoard.FAKE]: yield test_target.Extend(board.BoardName() + '-tests', board=board, app=HostApp.TESTS) + yield test_target.Extend(board.BoardName() + '-tests-clang', board=board, app=HostApp.TESTS, use_clang=True) def Esp32Targets(): diff --git a/scripts/build/testdata/build_linux_on_x64.txt b/scripts/build/testdata/build_linux_on_x64.txt index 15cb6388a0ca6f..5c02b32ff80cd1 100644 --- a/scripts/build/testdata/build_linux_on_x64.txt +++ b/scripts/build/testdata/build_linux_on_x64.txt @@ -144,6 +144,9 @@ PKG_CONFIG_PATH="SYSROOT_AARCH64/lib/aarch64-linux-gnu/pkgconfig" \ # Generating linux-fake-tests gn gen --check --fail-on-unused-args --export-compile-commands --root={root} '--args=chip_build_tests=true custom_toolchain="//build/toolchain/fake:fake_x64_gcc" chip_link_tests=true chip_device_platform="fake" chip_fake_platform=true' {out}/linux-fake-tests +# Generating linux-fake-tests-clang +gn gen --check --fail-on-unused-args --export-compile-commands --root={root} '--args=is_clang=true chip_build_tests=true custom_toolchain="//build/toolchain/fake:fake_x64_gcc" chip_link_tests=true chip_device_platform="fake" chip_fake_platform=true' {out}/linux-fake-tests-clang + # Generating linux-x64-address-resolve-tool gn gen --check --fail-on-unused-args --export-compile-commands --root={root} {out}/linux-x64-address-resolve-tool @@ -231,6 +234,9 @@ gn gen --check --fail-on-unused-args --export-compile-commands --root={root}/exa # Generating linux-x64-tests gn gen --check --fail-on-unused-args --export-compile-commands --root={root} --args=chip_build_tests=true {out}/linux-x64-tests +# Generating linux-x64-tests-clang +gn gen --check --fail-on-unused-args --export-compile-commands --root={root} '--args=is_clang=true chip_build_tests=true' {out}/linux-x64-tests-clang + # Generating linux-x64-thermostat gn gen --check --fail-on-unused-args --export-compile-commands --root={root}/examples/thermostat/linux {out}/linux-x64-thermostat @@ -336,6 +342,9 @@ ninja -C {out}/linux-arm64-tv-casting-app-ipv6only # Building linux-fake-tests ninja -C {out}/linux-fake-tests check +# Building linux-fake-tests-clang +ninja -C {out}/linux-fake-tests-clang check + # Building linux-x64-address-resolve-tool ninja -C {out}/linux-x64-address-resolve-tool src/lib/address_resolve:address-resolve-tool @@ -423,6 +432,9 @@ ninja -C {out}/linux-x64-shell-ipv6only # Building linux-x64-tests ninja -C {out}/linux-x64-tests check +# Building linux-x64-tests-clang +ninja -C {out}/linux-x64-tests-clang check + # Building linux-x64-thermostat ninja -C {out}/linux-x64-thermostat diff --git a/src/credentials/tests/BUILD.gn b/src/credentials/tests/BUILD.gn index 3e0082903e4802..f4fb7845271ed8 100644 --- a/src/credentials/tests/BUILD.gn +++ b/src/credentials/tests/BUILD.gn @@ -17,6 +17,7 @@ import("//build_overrides/chip.gni") import("//build_overrides/nlunit_test.gni") import("${chip_root}/build/chip/chip_test_suite.gni") +import("${chip_root}/build/chip/fuzz_test.gni") static_library("cert_test_vectors") { output_name = "libCertTestVectors" @@ -61,3 +62,10 @@ chip_test_suite("tests") { "${nlunit_test_root}:nlunit-test", ] } + +if (enable_fuzz_test_targets) { + chip_fuzz_target("fuzz-chip-cert") { + sources = [ "FuzzChipCert.cpp" ] + public_deps = [ "${chip_root}/src/credentials" ] + } +} diff --git a/src/credentials/tests/FuzzChipCert.cpp b/src/credentials/tests/FuzzChipCert.cpp new file mode 100644 index 00000000000000..fc983034627611 --- /dev/null +++ b/src/credentials/tests/FuzzChipCert.cpp @@ -0,0 +1,36 @@ +#include +#include + +#include "credentials/CHIPCert.h" + +using namespace chip; +using namespace chip::Credentials; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t * data, size_t len) +{ + + NodeId nodeId; + FabricId fabricId; + + ByteSpan span(data, len); + + (void) ExtractFabricIdFromCert(span, &fabricId); + (void) ExtractNodeIdFabricIdFromOpCert(span, &nodeId, &fabricId); + + { + ChipDN dn; + (void) ExtractSubjectDNFromX509Cert(span, dn); + } + + { + Credentials::P256PublicKeySpan key; + (void) ExtractPublicKeyFromChipCert(span, key); + } + + { + ChipCertificateData certData; + (void) DecodeChipCert(span, certData); + } + + return 0; +} diff --git a/src/lib/core/tests/BUILD.gn b/src/lib/core/tests/BUILD.gn index 86120cd3a5ae53..74e2cb932fbcda 100644 --- a/src/lib/core/tests/BUILD.gn +++ b/src/lib/core/tests/BUILD.gn @@ -17,6 +17,7 @@ import("//build_overrides/chip.gni") import("//build_overrides/nlunit_test.gni") import("${chip_root}/build/chip/chip_test_suite.gni") +import("${chip_root}/build/chip/fuzz_test.gni") chip_test_suite("tests") { output_name = "libCoreTests" @@ -39,3 +40,10 @@ chip_test_suite("tests") { "${nlunit_test_root}:nlunit-test", ] } + +if (enable_fuzz_test_targets) { + chip_fuzz_target("fuzz-tlv-reader") { + sources = [ "FuzzTlvReader.cpp" ] + public_deps = [ "${chip_root}/src/lib/core" ] + } +} diff --git a/src/lib/core/tests/FuzzTlvReader.cpp b/src/lib/core/tests/FuzzTlvReader.cpp new file mode 100644 index 00000000000000..2628d661e7c53c --- /dev/null +++ b/src/lib/core/tests/FuzzTlvReader.cpp @@ -0,0 +1,21 @@ +#include +#include + +#include "lib/core/CHIPTLV.h" +#include "lib/core/CHIPTLVUtilities.hpp" + +using chip::TLV::TLVReader; + +static CHIP_ERROR FuzzIterator(const TLVReader & aReader, size_t aDepth, void * aContext) +{ + return CHIP_NO_ERROR; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t * data, size_t len) +{ + TLVReader reader; + reader.Init(data, len); + chip::TLV::Utilities::Iterate(reader, FuzzIterator, nullptr); + + return 0; +} diff --git a/src/lib/dnssd/minimal_mdns/tests/BUILD.gn b/src/lib/dnssd/minimal_mdns/tests/BUILD.gn index d11b2ce96226d6..d99838ba1fb8af 100644 --- a/src/lib/dnssd/minimal_mdns/tests/BUILD.gn +++ b/src/lib/dnssd/minimal_mdns/tests/BUILD.gn @@ -17,6 +17,7 @@ import("//build_overrides/chip.gni") import("//build_overrides/nlunit_test.gni") import("${chip_root}/build/chip/chip_test_suite.gni") +import("${chip_root}/build/chip/fuzz_test.gni") chip_test_suite("tests") { output_name = "libMinimalMdnstests" @@ -42,3 +43,10 @@ chip_test_suite("tests") { "${nlunit_test_root}:nlunit-test", ] } + +if (enable_fuzz_test_targets) { + chip_fuzz_target("fuzz-minmdns-packet-parsing") { + sources = [ "FuzzPacketParsing.cpp" ] + public_deps = [ "${chip_root}/src/lib/dnssd/minimal_mdns" ] + } +} diff --git a/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsing.cpp b/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsing.cpp new file mode 100644 index 00000000000000..fbbfaa941c6c0e --- /dev/null +++ b/src/lib/dnssd/minimal_mdns/tests/FuzzPacketParsing.cpp @@ -0,0 +1,65 @@ +#include +#include + +#include +#include + +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 + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t * data, size_t len) +{ + + BytesRange packet(data, data + len); + FuzzDelegate delegate(packet); + + mdns::Minimal::ParsePacket(packet, &delegate); + + return 0; +}