Skip to content
Open
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
13 changes: 9 additions & 4 deletions build/chip/fuzz_test.gni
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,15 @@ template("chip_fuzz_target") {
if (oss_fuzz) {
fuzz_configs += [ "//build/config/compiler:oss_fuzz" ]
} else {
fuzz_configs += [
"//build/config/compiler:libfuzzer_fuzzing",
"//build/config/compiler:sanitize_address",
]
fuzz_configs += [ "//build/config/compiler:libfuzzer_fuzzing" ]

# ASan is the default sanitizer for local libFuzzer builds. MSAN (is_msan) is
# mutually exclusive with ASan and is applied globally via sanitize_default
# (which pulls in the instrumented-sysroot libc++), so only add ASan here when
# not building MSAN.
if (!is_msan) {
fuzz_configs += [ "//build/config/compiler:sanitize_address" ]
}
}

if (defined(public_configs)) {
Expand Down
9 changes: 0 additions & 9 deletions build/config/compiler/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,6 @@ declare_args() {
# Enable Thread sanitizer
is_tsan = false

# Enable memory sanitizer
is_msan = false

# MSAN builds need an instrumented C++ runtime and dependencies.
# build_examples.py resolves this in host.py and passes it as a GN arg
# (default: ~/.cache/matter/msan_sysroot, overridable via SYSROOT_MSAN).
# Raw `gn gen` users must set SYSROOT_MSAN or pass --args='msan_sysroot="..."'.
msan_sysroot = getenv("SYSROOT_MSAN")

# enable undefined behavior sanitizer
is_ubsan = false

Expand Down
11 changes: 11 additions & 0 deletions build/config/compiler/compiler.gni
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ declare_args() {
# Enable address sanitizer
is_asan = false

# Enable memory sanitizer
is_msan = false

# MSAN builds need an instrumented C++ runtime and dependencies.
# build_examples.py resolves this in host.py and passes it as a GN arg
# (default: ~/.cache/matter/msan_sysroot, overridable via SYSROOT_MSAN).
# Raw `gn gen` users must set SYSROOT_MSAN or pass --args='msan_sysroot="..."'.
# Declared here (rather than in compiler/BUILD.gn) so the fuzz templates and the
# pw_fuzzer toolchain, which import this .gni, can read is_msan.
msan_sysroot = getenv("SYSROOT_MSAN")

# Debug prefix mapping (values for -fdebug-prefix-map=).
prefix_mappings = []

Expand Down
19 changes: 19 additions & 0 deletions build/toolchain/pw_fuzzer/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,18 @@ declare_args() {
# (set by the `ubsan` build_examples modifier on a -pw-fuzztest target). Local builds only;
# under OSS-Fuzz the engine drives sanitizers via $CFLAGS.
chip_pw_fuzz_ubsan = false

# Build the local pw_fuzzer / FuzzTest targets with MemorySanitizer instead of the default
# ASan (set by the `msan` build_examples modifier on a -pw-fuzztest target). Requires an
# instrumented sysroot via msan_sysroot. Local builds only; under OSS-Fuzz the engine
# drives sanitizers via $CFLAGS.
chip_pw_fuzz_msan = false
}

assert(
!chip_pw_fuzz_msan || msan_sysroot != "",
"chip_pw_fuzz_msan=true requires msan_sysroot to be set. Build via " + "scripts/build/build_examples.py --target ...-pw-fuzztest-msan-clang, " + "or set SYSROOT_MSAN before `gn gen`.")

# --- libFuzzer-compatibility-mode support for FuzzTest ---
# Applied toolchain-wide (via _use_libfuzzer_compat) so the configs below reach the FuzzTest
# library, not just the test harness.
Expand Down Expand Up @@ -144,6 +154,15 @@ gcc_toolchain("chip_pw_fuzztest") {
"$dir_pw_fuzzer:instrumentation",
"$dir_pw_toolchain/host_clang:sanitize_address",
]
} else if (chip_pw_fuzz_msan) {
# Local MSAN: swap pigweed's plain -fsanitize=address for chip's MemorySanitizer
# config (ASan and MSAN are mutually exclusive). sanitize_memory pulls the
# MSAN-instrumented libc++ from msan_sysroot and applies msan_ignorelist.txt. Pigweed's
# coverage instrumentation ($dir_pw_fuzzer:instrumentation) is compatible with MSAN and
# is kept for coverage-guided runs.
remove_default_configs +=
[ "$dir_pw_toolchain/host_clang:sanitize_address" ]
default_configs += [ "//build/config/compiler:sanitize_memory" ]
} else {
# Local builds keep pigweed's base -fsanitize=address. Optional UBSan on top via the
# `ubsan` modifier on a -pw-fuzztest target.
Expand Down
2 changes: 1 addition & 1 deletion scripts/build/build/targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ def BuildHostTarget():
target.AppendModifier("tsan", use_tsan=True).ExceptIfRe("-asan")
target.AppendModifier("ubsan", use_ubsan=True)
target.AppendModifier("msan", use_msan=True).OnlyIfRe("-clang").OnlyIfRe("-x64").ExceptIfRe(
"-(asan|tsan|ubsan|libfuzzer|ossfuzz|pw-fuzztest)")
"-(asan|tsan|ubsan|ossfuzz)")
target.AppendModifier("libfuzzer", fuzzing_type=HostFuzzingType.LIB_FUZZER).OnlyIfRe(
"-clang").ExceptIfRe('-ossfuzz')
target.AppendModifier("ossfuzz", pw_fuzz_libfuzzer_compat=True).OnlyIfRe("-pw-fuzztest")
Expand Down
116 changes: 78 additions & 38 deletions scripts/build/build_msan_sysroot.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ DEFAULT_SYSROOT="$HOME/.cache/matter/msan_sysroot"
IGNORELIST="$CHIP_ROOT/build/config/compiler/msan_ignorelist.txt"
SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd)/$(basename "$0")"
SRC="${TMPDIR:-/tmp}/msan-build"
MSAN="-fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -fPIC"
# MSAN flags and the dep compiler are mode-dependent (local vs --oss-fuzz); computed below
# once arguments and the environment have been parsed.

# Pinned for reproducibility and to keep msan_ignorelist.txt source paths
# matching. On bump: verify clean MSAN build, no new false positives, and
Expand All @@ -55,7 +56,7 @@ GLIB_SERIES="${GLIB_VERSION%.*}" # GNOME download path uses major.minor (e.g. 2.

usage() {
cat <<EOF
Usage: $(basename "$0") [--out-dir PATH] [--force] [--check] [--help]
Usage: $(basename "$0") [--out-dir PATH] [--force] [--check] [--oss-fuzz] [--help]

Builds an MSan-instrumented sysroot for Matter MSAN tests.

Expand All @@ -64,6 +65,10 @@ Options:
--force Rebuild even if the existing sysroot is current
--check Report freshness only; exit 0 if current, 2 if missing,
3 if stale. Does not start a build.
--oss-fuzz Build for an OSS-Fuzz container: use the OSS-Fuzz compiler
(\$CC/\$CXX) and \$CFLAGS (which already carry -fsanitize=memory),
and SKIP building libc++ (OSS-Fuzz ships its own instrumented
libc++ at /usr/msan). Builds only the C dependencies.
--help Show this message

Environment:
Expand All @@ -82,6 +87,7 @@ EOF

FORCE=0
CHECK_ONLY=0
OSS_FUZZ=0
OUT_DIR=""
while [[ $# -gt 0 ]]; do
case "$1" in
Expand All @@ -97,6 +103,10 @@ while [[ $# -gt 0 ]]; do
CHECK_ONLY=1
shift
;;
--oss-fuzz)
OSS_FUZZ=1
shift
;;
--help | -h)
usage
exit 0
Expand All @@ -113,20 +123,36 @@ SYSROOT="${OUT_DIR:-${SYSROOT_MSAN:-$DEFAULT_SYSROOT}}"
STAMP="$SYSROOT/.build_complete"
LOCKFILE="$SYSROOT/.build.lock"

if [[ ! -x "$PW_CLANG" ]] || [[ ! -x "$PW_CLANGXX" ]]; then
echo "ERROR: Pigweed clang/clang++ not found on PATH" >&2
echo " clang: ${PW_CLANG:-<none>}" >&2
echo " clang++: ${PW_CLANGXX:-<none>}" >&2
echo "Run: source scripts/activate.sh" >&2
exit 1
fi
# Resolve the dependency compiler and the MSAN compile flags.
# Local: Pigweed's clang builds an instrumented libc++ and the C deps; the
# "is this Pigweed's clang" guard below catches a forgotten activate.sh.
# OSS-Fuzz: the container provides $CC/$CXX and $CFLAGS (already carrying
# -fsanitize=memory -fsanitize-memory-track-origins). libc++ is shipped
# instrumented at /usr/msan, so it is NOT built here.
if [[ "$OSS_FUZZ" -eq 1 ]]; then
CC_BIN="${CC:-clang}"
CXX_BIN="${CXX:-clang++}"
MSAN="${CFLAGS:-} -fPIC"
else
if [[ ! -x "$PW_CLANG" ]] || [[ ! -x "$PW_CLANGXX" ]]; then
echo "ERROR: Pigweed clang/clang++ not found on PATH" >&2
echo " clang: ${PW_CLANG:-<none>}" >&2
echo " clang++: ${PW_CLANGXX:-<none>}" >&2
echo "Run: source scripts/activate.sh" >&2
exit 1
fi

LLVM_COMMIT="$("$PW_CLANG" --version | grep -oP '[0-9a-f]{40}' || true)"
if [[ -z "$LLVM_COMMIT" ]]; then
echo "ERROR: clang on PATH is not Pigweed's (no LLVM commit in --version)" >&2
echo " clang: $PW_CLANG" >&2
echo "Run: source scripts/activate.sh" >&2
exit 1
LLVM_COMMIT="$("$PW_CLANG" --version | grep -oP '[0-9a-f]{40}' || true)"
if [[ -z "$LLVM_COMMIT" ]]; then
echo "ERROR: clang on PATH is not Pigweed's (no LLVM commit in --version)" >&2
echo " clang: $PW_CLANG" >&2
echo "Run: source scripts/activate.sh" >&2
exit 1
fi

CC_BIN="$PW_CLANG"
CXX_BIN="$PW_CLANGXX"
MSAN="-fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -fPIC"
fi

if [[ ! -f "$IGNORELIST" ]]; then
Expand All @@ -143,7 +169,7 @@ compute_input_hash() {
{
sha256sum <"$SCRIPT_PATH" | cut -d' ' -f1
sha256sum <"$IGNORELIST" | cut -d' ' -f1
"$PW_CLANG" --version
"$CC_BIN" --version
} | sha256sum | cut -d' ' -f1
}

Expand Down Expand Up @@ -197,28 +223,32 @@ trap on_error ERR

echo ">>> Building MSAN sysroot at $SYSROOT (this takes 5-15 min)"

# libc++ / libc++abi (MSan-instrumented)
echo ">>> libc++ / libc++abi"
if [[ ! -d "$SRC/llvm-project" ]]; then
git clone --depth 1 https://llvm.googlesource.com/llvm-project "$SRC/llvm-project"
# libc++ / libc++abi (MSan-instrumented). Skipped under --oss-fuzz: the OSS-Fuzz base image
# already ships an instrumented libc++ at /usr/msan built with its own clang, and reusing
# ours (built with Pigweed clang) would mismatch the fuzzer's compiler.
if [[ "$OSS_FUZZ" -eq 0 ]]; then
echo ">>> libc++ / libc++abi"
if [[ ! -d "$SRC/llvm-project" ]]; then
git clone --depth 1 https://llvm.googlesource.com/llvm-project "$SRC/llvm-project"
fi
cd "$SRC/llvm-project"
git fetch --depth 1 origin "$LLVM_COMMIT"
git checkout FETCH_HEAD
cmake -GNinja -S "$SRC/llvm-project/runtimes" -B "$SRC/libcxx" \
-DCMAKE_C_COMPILER="$CC_BIN" -DCMAKE_CXX_COMPILER="$CXX_BIN" \
-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" -DCMAKE_BUILD_TYPE=Release \
-DLLVM_USE_SANITIZER=MemoryWithOrigins -DCMAKE_INSTALL_PREFIX="$SYSROOT" \
-DLIBCXX_ENABLE_SHARED=OFF -DLIBCXXABI_ENABLE_SHARED=OFF -DLIBCXXABI_USE_LLVM_UNWINDER=OFF
ninja -C "$SRC/libcxx" -j"$(nproc)" cxx cxxabi
ninja -C "$SRC/libcxx" install-cxx install-cxxabi
fi
cd "$SRC/llvm-project"
git fetch --depth 1 origin "$LLVM_COMMIT"
git checkout FETCH_HEAD
cmake -GNinja -S "$SRC/llvm-project/runtimes" -B "$SRC/libcxx" \
-DCMAKE_C_COMPILER="$PW_CLANG" -DCMAKE_CXX_COMPILER="$PW_CLANGXX" \
-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" -DCMAKE_BUILD_TYPE=Release \
-DLLVM_USE_SANITIZER=MemoryWithOrigins -DCMAKE_INSTALL_PREFIX="$SYSROOT" \
-DLIBCXX_ENABLE_SHARED=OFF -DLIBCXXABI_ENABLE_SHARED=OFF -DLIBCXXABI_USE_LLVM_UNWINDER=OFF
ninja -C "$SRC/libcxx" -j"$(nproc)" cxx cxxabi
ninja -C "$SRC/libcxx" install-cxx install-cxxabi

# OpenSSL
echo ">>> OpenSSL"
cd "$SRC"
[[ -d "openssl-$OPENSSL_VERSION" ]] || { wget -q "https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz" && tar xzf "openssl-$OPENSSL_VERSION.tar.gz"; }
cd "openssl-$OPENSSL_VERSION"
CC="$PW_CLANG $MSAN" ./Configure linux-x86_64 --prefix="$SYSROOT" --openssldir="$SYSROOT/ssl" no-asm no-shared -DPURIFY
CC="$CC_BIN $MSAN" ./Configure linux-x86_64 --prefix="$SYSROOT" --openssldir="$SYSROOT/ssl" no-asm no-shared -DPURIFY
make -j"$(nproc)" build_libs
make install_dev

Expand All @@ -227,15 +257,15 @@ echo ">>> zlib"
cd "$SRC"
[[ -d "zlib-$ZLIB_VERSION" ]] || { wget -q -O - "https://github.com/madler/zlib/releases/download/v$ZLIB_VERSION/zlib-$ZLIB_VERSION.tar.gz" | tar xz; }
cd "zlib-$ZLIB_VERSION"
CC="$PW_CLANG" CFLAGS="$MSAN" LDFLAGS="-fsanitize=memory" ./configure --prefix="$SYSROOT" --static
CC="$CC_BIN" CFLAGS="$MSAN" LDFLAGS="-fsanitize=memory ${LDFLAGS:-}" ./configure --prefix="$SYSROOT" --static
make -j"$(nproc)" && make install

# libffi
echo ">>> libffi"
cd "$SRC"
[[ -d "libffi-$LIBFFI_VERSION" ]] || { wget -q "https://github.com/libffi/libffi/releases/download/v$LIBFFI_VERSION/libffi-$LIBFFI_VERSION.tar.gz" && tar xzf "libffi-$LIBFFI_VERSION.tar.gz"; }
cd "libffi-$LIBFFI_VERSION"
CC="$PW_CLANG" CXX="$PW_CLANGXX" CFLAGS="$MSAN" CXXFLAGS="$MSAN" LDFLAGS="-fsanitize=memory" \
CC="$CC_BIN" CXX="$CXX_BIN" CFLAGS="$MSAN" CXXFLAGS="$MSAN" LDFLAGS="-fsanitize=memory ${LDFLAGS:-}" \
./configure --prefix="$SYSROOT" --disable-shared --enable-static --quiet
make -j"$(nproc)" && make install

Expand All @@ -244,7 +274,7 @@ echo ">>> pcre2"
cd "$SRC"
[[ -d "pcre2-$PCRE2_VERSION" ]] || { wget -q "https://github.com/PCRE2Project/pcre2/releases/download/pcre2-$PCRE2_VERSION/pcre2-$PCRE2_VERSION.tar.gz" && tar xzf "pcre2-$PCRE2_VERSION.tar.gz"; }
cd "pcre2-$PCRE2_VERSION"
CC="$PW_CLANG" CXX="$PW_CLANGXX" CFLAGS="$MSAN" CXXFLAGS="$MSAN" LDFLAGS="-fsanitize=memory" \
CC="$CC_BIN" CXX="$CXX_BIN" CFLAGS="$MSAN" CXXFLAGS="$MSAN" LDFLAGS="-fsanitize=memory ${LDFLAGS:-}" \
./configure --prefix="$SYSROOT" --disable-shared --enable-static --quiet
make -j"$(nproc)" && make install

Expand All @@ -253,17 +283,27 @@ echo ">>> GLib"
cd "$SRC"
[[ -d "glib-$GLIB_VERSION" ]] || { wget -q "https://download.gnome.org/sources/glib/$GLIB_SERIES/glib-$GLIB_VERSION.tar.xz" && tar xf "glib-$GLIB_VERSION.tar.xz"; }

# Meson wants the flags as a quoted array (one element per flag). Split $MSAN on whitespace
# into individual flags so the GLib build uses the same flag set as the other deps (the
# OSS-Fuzz $CFLAGS or the local default). NOTE: $MSAN must NOT be a single quoted word here,
# or meson collapses every flag into one c_args element and clang rejects it
# ("unsupported argument ... to option '-fsanitize='"). read -ra splits explicitly and keeps
# shellcheck happy (vs. an unquoted $MSAN, which a linter is prone to "fix" back into one word).
read -ra _msan_flags <<<"$MSAN"
_msan_meson=""
for _f in "${_msan_flags[@]}"; do _msan_meson+="'$_f', "; done

cat >"$SRC/msan-native.ini" <<EOF
[binaries]
c = '$PW_CLANG'
cpp = '$PW_CLANGXX'
c = '$CC_BIN'
cpp = '$CXX_BIN'
ar = 'ar'
strip = 'strip'
pkgconfig = 'pkg-config'

[built-in options]
c_args = ['-fsanitize=memory', '-fsanitize-memory-track-origins', '-fno-omit-frame-pointer', '-fPIC', '-Wno-error=implicit-function-declaration', '-fsanitize-ignorelist=$IGNORELIST']
cpp_args = ['-fsanitize=memory', '-fsanitize-memory-track-origins', '-fno-omit-frame-pointer', '-fPIC', '-fsanitize-ignorelist=$IGNORELIST']
c_args = [${_msan_meson}'-fsanitize-ignorelist=$IGNORELIST', '-Wno-error=implicit-function-declaration']
cpp_args = [${_msan_meson}'-fsanitize-ignorelist=$IGNORELIST']
c_link_args = ['-fsanitize=memory']
cpp_link_args = ['-fsanitize=memory']

Expand Down
8 changes: 7 additions & 1 deletion scripts/build/builders/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,13 @@ def __init__(self, root: str, runner: Runner, output_dir_lock: OutDirLock, app:
if use_msan:
if not runner.dry_run:
_msan_validate_sysroot(chip_root)
self.extra_gn_options.append('is_msan=true')
if fuzzing_type == HostFuzzingType.PW_FUZZTEST:
# pw_fuzzer FuzzTest targets build in the chip_pw_fuzztest secondary toolchain,
# which does not consume chip's global is_msan/sanitize_default. Drive MSAN via
# the toolchain arg instead (it swaps pigweed's ASan for chip's sanitize_memory).
self.extra_gn_options.append('chip_pw_fuzz_msan=true')
else:
self.extra_gn_options.append('is_msan=true')
# Tell GN to build against the same sysroot we just validated.
self.extra_gn_options.append(f'msan_sysroot="{_msan_sysroot_path()}"')

Expand Down
Loading