Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Rust coverage report (for Suricata) #4697

Merged
merged 2 commits into from
Mar 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion infra/base-images/base-builder/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ RUN curl https://sh.rustup.rs | sh -s -- -y --default-toolchain=nightly --profil
RUN cargo install cargo-fuzz
# Needed to recompile rust std library for MSAN
RUN rustup component add rust-src --toolchain nightly
# Set up custom environment variable for source code copy for coverage reports
ENV OSSFUZZ_RUSTPATH /rust

# Install Bazel through Bazelisk, which automatically fetches the latest Bazel version.
ENV BAZELISK_VERSION 1.7.4
Expand Down Expand Up @@ -185,7 +187,7 @@ RUN cd $SRC && \
tar -xzv --strip-components=1 -f $SRC/oss-fuzz.tar.gz && \
rm -rf examples $SRC/oss-fuzz.tar.gz

COPY compile compile_afl compile_dataflow compile_libfuzzer compile_honggfuzz \
COPY cargo compile compile_afl compile_dataflow compile_libfuzzer compile_honggfuzz \
compile_go_fuzzer precompile_honggfuzz precompile_afl debug_afl srcmap \
write_labels.py /usr/local/bin/

Expand Down
51 changes: 51 additions & 0 deletions infra/base-images/base-builder/cargo
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/bash -eu
# Copyright 2020 Google Inc.
#
# 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.
#
# This is a wrapper around calling cargo
# This just expands RUSTFLAGS in case of a coverage build
# We need this until https://github.com/rust-lang/cargo/issues/5450 is merged
# because cargo uses relative paths for the current crate
# and absolute paths for its dependencies
#
################################################################################

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hack is to use this cargo which come first in PATH
So we can change RUSTFLAGS for this execution
then execute /rust/bin/cargo

export PATH="/rust/bin:$PATH"

if [ "$SANITIZER" = "coverage" ] && [ $1 = "build" ]
then
crate_src_abspath=`cargo metadata --no-deps --format-version 1 | jq -r '.workspace_root'`
export RUSTFLAGS="$RUSTFLAGS --remap-path-prefix src=$crate_src_abspath/src"
fi

if [ "$SANITIZER" = "coverage" ] && [ $1 = "fuzz" ]
then
fuzz_src_abspath=`pwd`
export RUSTFLAGS="$RUSTFLAGS --remap-path-prefix fuzz_targets=$fuzz_src_abspath/fuzz_targets"
# hack to turn cargo fuzz build into cargo build so as to get coverage
# cargo fuzz adds "--target" "x86_64-unknown-linux-gnu"
(
# go into fuzz directory if not already the case
cd fuzz || true
# do not optimize with --release, leading to Malformed instrumentation profile data
cargo build --bins
# copies the build output in the expected target directory
cd target
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this is causing a build failure for wasmtime because we include the fuzz directory as part of the main workspace meaning that the target directory doesn't show up under the fuzz directory.

Perhaps this could use something like:

cargo metadata --format-version 1 --no-deps | jq '.target_directory'

to extract the target directory for the fuzz project?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another alternative, if the goal is simply to not optimize, is to set CARGO_PROFILE_RELEASE_OPT_LEVEL=0 and that should disable all optimizations without having to override Cargo?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @alexcrichton for debugging this cf #5366

Another alternative, if the goal is simply to not optimize

The goal is more than to not optimize
Indeed, we cannot optimize until rust-lang/rust#82144 is fixed
But, if I remember correctly, -Zinstrument-coverage seems incompatible with other cargo options set by cargo fuzz (like the llvm sancov pass, which would make sense)
So we turn cargo fuzz build into cargo build to be transparent to the existing build scripts

Another solution could be to have cargo fuzz accept a so-called coverage sanitizer which would set -Zinstrument-coverage
What do you think ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ideally yeah it'd be best to codify this in cargo fuzz itself which could automatically apply the defaults of not optimizing and anything else necessary (like removing those extra passes)

mkdir -p x86_64-unknown-linux-gnu/release
cp -r debug/* x86_64-unknown-linux-gnu/release/
)
exit 0
fi

cargo "$@"
7 changes: 6 additions & 1 deletion infra/base-images/base-builder/compile
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ if [ "$SANITIZER" != "undefined" ] && [ "$SANITIZER" != "coverage" ] && [ "$ARCH
else
export RUSTFLAGS="--cfg fuzzing -Cdebuginfo=1 -Cforce-frame-pointers"
fi
if [ "$SANITIZER" = "coverage" ]
then
# link to C++ from comment in f5098035eb1a14aa966c8651d88ea3d64323823d
export RUSTFLAGS="$RUSTFLAGS -Zinstrument-coverage -C link-arg=-lc++"
fi

# Add Rust libfuzzer flags.
# See https://github.com/rust-fuzz/libfuzzer/blob/master/build.rs#L12.
Expand Down Expand Up @@ -145,7 +150,7 @@ BUILD_CMD="bash -eux $SRC/build.sh"

# We need to preserve source code files for generating a code coverage report.
# We need exact files that were compiled, so copy both $SRC and $WORK dirs.
COPY_SOURCES_CMD="cp -rL --parents $SRC $WORK /usr/include /usr/local/include $GOPATH $OUT"
COPY_SOURCES_CMD="cp -rL --parents $SRC $WORK /usr/include /usr/local/include $GOPATH $OSSFUZZ_RUSTPATH $OUT"

if [ "${BUILD_UID-0}" -ne "0" ]; then
adduser -u $BUILD_UID --disabled-password --gecos '' builder
Expand Down
3 changes: 2 additions & 1 deletion infra/base-images/base-clang/checkout_build_install_llvm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
# 2).
NPROC=$(expr $(nproc) / 2)

LLVM_DEP_PACKAGES="build-essential make cmake ninja-build git python3 g++-multilib binutils-dev"
# zlib1g-dev is needed for llvm-profdata to handle coverage data from rust compiler
LLVM_DEP_PACKAGES="build-essential make cmake ninja-build git python3 g++-multilib binutils-dev zlib1g-dev"
apt-get install -y $LLVM_DEP_PACKAGES --no-install-recommends

# Checkout
Expand Down
9 changes: 9 additions & 0 deletions infra/base-images/base-runner/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
wget \
curl \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be there already now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same: I see it in base-builder but not base-runner...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be moved to base-image or some other generic image ?

zip

RUN git clone https://chromium.googlesource.com/chromium/src/tools/code_coverage /opt/code_coverage && \
Expand All @@ -83,6 +84,13 @@ ENV UBSAN_OPTIONS="print_stacktrace=1:print_summary=1:silence_unsigned_overflow=
ENV FUZZER_ARGS="-rss_limit_mb=2560 -timeout=25"
ENV AFL_FUZZER_ARGS="-m none"

# Install rustfilt for symbol demangling.
ENV CARGO_HOME=/rust
ENV RUSTUP_HOME=/rust/rustup
ENV PATH=$PATH:/rust/bin
RUN curl https://sh.rustup.rs | sh -s -- -y --default-toolchain=nightly
RUN cargo install rustfilt

# Install OpenJDK 15 and trim its size by removing unused components.
ENV JAVA_HOME=/usr/lib/jvm/java-15-openjdk-amd64
ENV JVM_LD_LIBRARY_PATH=$JAVA_HOME/lib/server
Expand All @@ -103,6 +111,7 @@ COPY bad_build_check \
dataflow_tracer.py \
download_corpus \
minijail0 \
rcfilt \
reproduce \
run_fuzzer \
run_minijail \
Expand Down
2 changes: 1 addition & 1 deletion infra/base-images/base-runner/coverage
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ else

# Generate HTML report.
llvm-cov show -format=html -output-dir=$REPORT_ROOT_DIR \
-Xdemangler c++filt -Xdemangler -n $LLVM_COV_ARGS
-Xdemangler rcfilt $LLVM_COV_ARGS

# Export coverage summary in JSON format.
llvm-cov export -summary-only $LLVM_COV_ARGS > $SUMMARY_FILE
Expand Down
21 changes: 21 additions & 0 deletions infra/base-images/base-runner/rcfilt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash -u
# Copyright 2020 Google Inc.
#
# 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.
#
# Symbol demangling for both C++ and Rust
#
################################################################################

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add file description here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

# simply pipe
rustfilt | c++filt -n
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to work for ecc-diff-fuzzer which has rust and C++

2 changes: 1 addition & 1 deletion infra/build/functions/build_and_run_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
UPLOAD_URL_FORMAT = 'gs://' + COVERAGE_BUCKET_NAME + '/{project}/{type}/{date}'

# Languages from project.yaml that have code coverage support.
LANGUAGES_WITH_COVERAGE_SUPPORT = ['c', 'c++', 'go']
LANGUAGES_WITH_COVERAGE_SUPPORT = ['c', 'c++', 'go', 'rust']


def usage():
Expand Down
2 changes: 1 addition & 1 deletion infra/ci/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
DEFAULT_SANITIZERS = ['address', 'undefined']

# Languages from project.yaml that have code coverage support.
LANGUAGES_WITH_COVERAGE_SUPPORT = ['c', 'c++', 'go']
LANGUAGES_WITH_COVERAGE_SUPPORT = ['c', 'c++', 'go', 'rust']


def get_changed_files_output():
Expand Down
2 changes: 1 addition & 1 deletion infra/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
PROJECT_LANGUAGE_REGEX = re.compile(r'\s*language\s*:\s*([^\s]+)')

# Languages from project.yaml that have code coverage support.
LANGUAGES_WITH_COVERAGE_SUPPORT = ['c', 'c++', 'go']
LANGUAGES_WITH_COVERAGE_SUPPORT = ['c', 'c++', 'go', 'rust']

# pylint: disable=too-many-lines

Expand Down
1 change: 1 addition & 0 deletions projects/suricata/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ RUN git clone --depth 1 https://github.com/OISF/libhtp.git libhtp
RUN git clone --depth 1 https://github.com/OISF/suricata-verify suricata-verify
WORKDIR $SRC
COPY build.sh $SRC/
COPY rustc.py $SRC/
10 changes: 9 additions & 1 deletion projects/suricata/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,16 @@ mv libhtp suricata/
cd suricata
sh autogen.sh
#run configure with right options
if [ "$SANITIZER" = "coverage" ]
then
export RUSTFLAGS="$RUSTFLAGS -C debug-assertions=no"
chmod +x $SRC/rustc.py
export RUSTC="$SRC/rustc.py"
./configure --disable-shared --enable-fuzztargets --enable-debug
else
./src/tests/fuzz/oss-fuzz-configure.sh
make
fi
make -j$(nproc)

cp src/fuzz_* $OUT/

Expand Down
28 changes: 28 additions & 0 deletions projects/suricata/rustc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python

# Copyright 2021 Google LLC
#
# 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 sys
import subprocess

#disable coverage for crate brotli_decompressor
sys.argv[0] = "rustc"
if "brotli_decompressor" in sys.argv:
try:
sys.argv.remove("-Zinstrument-coverage")
except:
pass
print(sys.argv)
subprocess.call(sys.argv)