Skip to content

Opus review

Opus review #139

Workflow file for this run

name: Sanitizers
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
concurrency:
group: sanitizers-${{ github.sha }}
cancel-in-progress: true
# All jobs run the full test suite via `python test_all.py`. C/C++ compile
# commands in tests/run_tests.py honor $CC/$CXX/$CFLAGS/$CXXFLAGS/$LDFLAGS, so
# the only thing each job has to do is export the right toolchain + flags.
#
# We deliberately skip language slices that have nothing to do with the
# instrumented C/C++ binaries (Rust/.NET) using --skip-lang to keep wall time
# manageable and avoid noise from unrelated toolchains.
jobs:
asan-ubsan:
name: AddressSanitizer + UndefinedBehaviorSanitizer (C / C++)
runs-on: ubuntu-latest
permissions:
contents: read
env:
CC: clang
CXX: clang++
# -fno-sanitize-recover so any UB aborts the test process with a
# non-zero status and is reported by run_tests.py.
CFLAGS: "-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined -fno-sanitize-recover=all"
CXXFLAGS: "-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined -fno-sanitize-recover=all"
LDFLAGS: "-fsanitize=address,undefined"
ASAN_OPTIONS: "detect_leaks=1:abort_on_error=1:halt_on_error=1:strict_string_checks=1:detect_stack_use_after_return=1"
UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1"
steps:
- uses: actions/checkout@v4
with: { submodules: true }
- uses: actions/setup-python@v5
with: { python-version: "3.11" }
- uses: actions/setup-node@v4
with: { node-version: "20" }
- name: Install toolchain
run: |
sudo apt-get update
sudo apt-get install -y clang llvm
pip install proto-schema-parser
(cd tests/ts && npm ci)
- name: Run C / C++ suite under ASan + UBSan
run: python test_all.py --skip-lang csharp --skip-lang rust
tsan:
name: ThreadSanitizer (C++ SDK)
runs-on: ubuntu-latest
permissions:
contents: read
env:
CC: clang
CXX: clang++
CFLAGS: "-O1 -g -fno-omit-frame-pointer -fsanitize=thread"
CXXFLAGS: "-O1 -g -fno-omit-frame-pointer -fsanitize=thread"
LDFLAGS: "-fsanitize=thread"
TSAN_OPTIONS: "halt_on_error=1:second_deadlock_stack=1"
steps:
- uses: actions/checkout@v4
with: { submodules: true }
- uses: actions/setup-python@v5
with: { python-version: "3.11" }
- uses: actions/setup-node@v4
with: { node-version: "20" }
- name: Install toolchain
run: |
sudo apt-get update
sudo apt-get install -y clang llvm
pip install proto-schema-parser
(cd tests/ts && npm ci)
- name: Run C / C++ suite under TSan
# TSan is mutually exclusive with ASan; the SDK subscribe / streaming
# tests are where the threading races would surface.
run: python test_all.py --skip-lang csharp --skip-lang rust
valgrind-c:
name: Valgrind memcheck (C only)
runs-on: ubuntu-latest
permissions:
contents: read
env:
CC: gcc
# No sanitizer flags — Valgrind instruments at run time. -O0 -g keeps
# the line numbers in error reports useful.
CFLAGS: "-O0 -g"
steps:
- uses: actions/checkout@v4
with: { submodules: true }
- uses: actions/setup-python@v5
with: { python-version: "3.11" }
- name: Install Valgrind
run: |
sudo apt-get update
sudo apt-get install -y valgrind gcc build-essential
pip install proto-schema-parser
- name: Build C binaries (compile only, no run)
# --only-compile produces all language artifacts; we only execute the
# C ones below under Valgrind. Other languages' compile failures are
# not relevant to this job, so we tolerate a non-zero exit here and
# rely on the subsequent "no C binaries produced" guard to fail loudly
# if the C portion didn't build.
run: python test_all.py --only-compile || echo "non-C languages may have failed; continuing"
- name: Run C binaries under Valgrind
# Run every produced C test binary under memcheck. --error-exitcode=1
# converts any leak / invalid-access into a non-zero CI status.
run: |
set -e
shopt -s nullglob
found=0
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
for exe in tests/c/build/test_*; do
[ -x "$exe" ] || continue
found=1
echo "=== valgrind $exe ==="
case "$(basename "$exe")" in
test_standard|test_standard.exe)
valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all \
--errors-for-leak-kinds=definite,possible \
--track-origins=yes "$exe" encode standard "$tmp_dir/standard.bin"
valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all \
--errors-for-leak-kinds=definite,possible \
--track-origins=yes "$exe" decode standard "$tmp_dir/standard.bin"
;;
test_extended|test_extended.exe)
valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all \
--errors-for-leak-kinds=definite,possible \
--track-origins=yes "$exe" encode bulk "$tmp_dir/extended.bin"
valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all \
--errors-for-leak-kinds=definite,possible \
--track-origins=yes "$exe" decode bulk "$tmp_dir/extended.bin"
;;
test_variable_flag|test_variable_flag.exe)
valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all \
--errors-for-leak-kinds=definite,possible \
--track-origins=yes "$exe" encode bulk "$tmp_dir/variable.bin"
valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all \
--errors-for-leak-kinds=definite,possible \
--track-origins=yes "$exe" decode bulk "$tmp_dir/variable.bin"
;;
*)
valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all \
--errors-for-leak-kinds=definite,possible \
--track-origins=yes "$exe"
;;
esac
done
if [ "$found" -eq 0 ]; then
echo "No C test binaries were built; nothing to check." >&2
exit 1
fi