Skip to content

Commit

Permalink
Add workflow for doing fuzzing builds. (#13670)
Browse files Browse the repository at this point in the history
* Add workflow for doing fuzzing builds.

Summary of changes:

1) examples/all-clusters-app/all-clusters-common/BUILD.gn: Makes
all-clusters-app compile when compiled on its own, without any client
apps (like chip-tool) as part of the same build.  It used to be
sneaking in a use_default_client_callbacks from someone else's
configuration, as best I can tell.

2) build/config/compiler/BUILD.gn: Add a define in the fuzzer config
so we can compile slightly different main() code.

3) examples/all-clusters-app/linux/main.cpp: Add a fuzzer hook that
tries to process the fuzzer-provided data as a message.

4) scripts/build: Add support for a "libfuzzer" variant.

* Addressing review comments.

* Add clean shutdown when the fuzzer decides to stop.
  • Loading branch information
bzbarsky-apple authored Jan 20, 2022
1 parent aa72854 commit d9b77e9
Show file tree
Hide file tree
Showing 10 changed files with 373 additions and 103 deletions.
131 changes: 131 additions & 0 deletions .github/workflows/fuzzing-build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Copyright (c) 2021 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.

name: Fuzzing Builds

on:
# For now, only manual triggers.
workflow_dispatch:

concurrency:
group: ${{ github.ref }}-${{ github.workflow }}-${{ (github.event_name == 'pull_request' && github.event.number) || (github.event_name == 'workflow_dispatch' && github.run_number) || github.sha }}
cancel-in-progress: true

jobs:
build_linux_fuzzing:
name: Build on Linux
timeout-minutes: 90

runs-on: ubuntu-latest
if: github.actor != 'restyled-io[bot]'

container:
image: connectedhomeip/chip-build:0.5.48
volumes:
- "/tmp/log_output:/tmp/test_logs"

steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- run: apt-get update
- run: apt-get install --fix-missing llvm-10 clang-10
- name:
Try to ensure the objdir-clone dir exists
run: |
mkdir objdir-clone || true
- name: Bootstrap
timeout-minutes: 10
run: scripts/build/gn_bootstrap.sh
- name: Uploading bootstrap logs
uses: actions/upload-artifact@v2
if: ${{ always() }} && ${{ !env.ACT }}
with:
name: bootstrap-logs
path: |
.environment/gn_out/.ninja_log
.environment/pigweed-venv/*.log
- name: Build all-clusters-app
timeout-minutes: 20
run: |
./scripts/run_in_build_env.sh \
"./scripts/build/build_examples.py \
--target linux-x64-all-clusters-no-ble-libfuzzer-test-group \
build \
--copy-artifacts-to objdir-clone \
"
- name: Uploading binaries
uses: actions/upload-artifact@v2
if: ${{ !env.ACT }}
with:
name:
objdir-linux
path: objdir-clone/
# objdirs are big; don't hold on to them too long.
retention-days: 5

build_darwin_fuzzing:
name: Build on Darwin
timeout-minutes: 90
runs-on: macos-latest
if: github.actor != 'restyled-io[bot]'

steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Setup Environment
run: brew install openssl pkg-config llvm
- name: Try to ensure the objdir-clone dir exists
run: |
mkdir objdir-clone || true
- name: Fix pkgconfig link
working-directory: /usr/local/lib/pkgconfig
run: |
pwd
ls -la /usr/local/Cellar/
ls -la /usr/local/Cellar/[email protected]
OPEN_SSL_VERSION=`ls -la /usr/local/Cellar/[email protected] | cat | tail -n1 | awk '{print $NF}'`
ln -s /usr/local/Cellar/[email protected]/$OPEN_SSL_VERSION/lib/pkgconfig/* .
- name: Bootstrap
timeout-minutes: 25
run: scripts/build/gn_bootstrap.sh
- name: Uploading bootstrap logs
uses: actions/upload-artifact@v2
if: ${{ always() }} && ${{ !env.ACT }}
with:
name: bootstrap-logs
path: |
.environment/gn_out/.ninja_log
.environment/pigweed-venv/*.log
- name: Build all-clusters-app
timeout-minutes: 20
run: |
./scripts/run_in_build_env.sh \
"./scripts/build/build_examples.py \
--target darwin-x64-all-clusters-no-ble-asan-libfuzzer \
build \
--copy-artifacts-to objdir-clone \
"
- name: Uploading binaries
uses: actions/upload-artifact@v2
if: ${{ !env.ACT }}
with:
name:
crash-darwin
path: objdir-clone/
# objdirs are big; don't hold on to them too long.
retention-days: 5
3 changes: 0 additions & 3 deletions build/config/compiler/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,6 @@ declare_args() {
# enable undefined behavior sanitizer
is_ubsan = false

# enable libfuzzer
is_libfuzzer = false

# Exit on sanitize error. Generally standard libraries may get errors
# so not stopping on the first error is often useful
is_sanitize_fatal = true
Expand Down
3 changes: 3 additions & 0 deletions build/config/compiler/compiler.gni
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@ declare_args() {

# C++ standard level (value for -std flag).
cpp_standard = "gnu++14"

# enable libfuzzer
is_libfuzzer = false
}
1 change: 1 addition & 0 deletions examples/all-clusters-app/all-clusters-common/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ chip_data_model("all-clusters-common") {
zap_pregenerated_dir =
"${chip_root}/zzz_generated/all-clusters-app/zap-generated"
is_server = true
use_default_client_callbacks = true
}
43 changes: 39 additions & 4 deletions examples/all-clusters-app/linux/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,18 @@

import("//build_overrides/build.gni")
import("//build_overrides/chip.gni")
import("${build_root}/config/compiler/compiler.gni")

import("${chip_root}/src/lib/lib.gni")

executable("chip-all-clusters-app") {
source_set("chip-all-clusters-common") {
sources = [
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/binding-handler.cpp",
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/bridged-actions-stub.cpp",
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp",
"include/tv-callbacks.cpp",
"include/tv-callbacks.h",
"main.cpp",
"main-common.cpp",
]

deps = [
Expand All @@ -41,10 +42,44 @@ executable("chip-all-clusters-app") {
if (chip_build_libshell) {
defines = [ "ENABLE_CHIP_SHELL" ]
}
}

if (is_libfuzzer) {
executable("chip-all-clusters-app-fuzzing") {
sources = [ "fuzzing-main.cpp" ]

deps = [
":chip-all-clusters-common",
"${chip_root}/examples/platform/linux:app-main",
]

cflags = [ "-Wconversion" ]

output_dir = root_out_dir
}
} else {
executable("chip-all-clusters-app") {
sources = [ "main.cpp" ]

output_dir = root_out_dir
deps = [
":chip-all-clusters-common",
"${chip_root}/examples/platform/linux:app-main",
]

cflags = [ "-Wconversion" ]

include_dirs =
[ "${chip_root}/examples/all-clusters-app/all-clusters-common/include" ]

output_dir = root_out_dir
}
}

group("linux") {
deps = [ ":chip-all-clusters-app" ]
deps = []
if (is_libfuzzer) {
deps += [ ":chip-all-clusters-app-fuzzing" ]
} else {
deps += [ ":chip-all-clusters-app" ]
}
}
73 changes: 73 additions & 0 deletions examples/all-clusters-app/linux/fuzzing-main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2022 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 "AppMain.h"
#include <app/server/Server.h>

using namespace chip;
using namespace chip::DeviceLayer;

void CleanShutdown()
{
Server::GetInstance().Shutdown();
PlatformMgr().Shutdown();
// TODO: We don't Platform::MemoryShutdown because ~CASESessionManager calls
// Dnssd::ResolverProxy::Shutdown, which starts doing Platform::Delete.
// Platform::MemoryShutdown();
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t * aData, size_t aSize)
{
static bool matterStackInitialized = false;
if (!matterStackInitialized)
{
// Might be simpler to do ChipLinuxAppInit() with argc == 0, argv set to
// just a fake executable name?
VerifyOrDie(Platform::MemoryInit() == CHIP_NO_ERROR);
VerifyOrDie(PlatformMgr().InitChipStack() == CHIP_NO_ERROR);

// ChipLinuxAppMainLoop blocks, and we don't want that here.
VerifyOrDie(Server::GetInstance().Init() == CHIP_NO_ERROR);

ApplicationInit();

// We don't start the event loop task, because we don't plan to deliver
// data on a separate thread.

matterStackInitialized = true;

// The fuzzer does not have a way to tell us when it's done, so just
// shut down things on exit.
atexit(CleanShutdown);
}

// For now, just dump the data as a UDP payload into the session manager.
// But maybe we should try to separately extract a PeerAddress and data from
// the incoming data?
Transport::PeerAddress peerAddr;
System::PacketBufferHandle buf =
System::PacketBufferHandle::NewWithData(aData, aSize, /* aAdditionalSize = */ 0, /* aReservedSize = */ 0);

// Ignoring the return value from OnMessageReceived, because we might be
// passing it all sorts of garbage that will cause it to fail.
Server::GetInstance().GetSecureSessionManager().OnMessageReceived(peerAddr, std::move(buf));

// Now process pending events until our sentinel is reached.
PlatformMgr().ScheduleWork([](intptr_t) { PlatformMgr().StopEventLoopTask(); });
PlatformMgr().RunEventLoop();
return 0;
}
Loading

0 comments on commit d9b77e9

Please sign in to comment.