fix(daemon): give capture measurements in a reply reason to root only #1942
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # AddressSanitizer over the test suite. | |
| # | |
| # irlume is mostly safe Rust, so this is aimed at the places where it is not: | |
| # the NSS `getpwnam_r`/`getpwuid_r` buffer handling, the mlock/madvise page | |
| # arithmetic that protects a decrypted secret, `secure_getenv`, the UVC XU | |
| # control ioctl, and the SO_PEERCRED socket path the daemon authorizes callers | |
| # with. Those are a few dozen lines, but they are the lines where a mistake is | |
| # a memory-safety bug in a program that authenticates people, and clippy cannot | |
| # see into them. | |
| # | |
| # Deliberately NOT a required check, and deliberately not on the fast path: it | |
| # needs a nightly toolchain (`-Zsanitizer` is unstable), so an unrelated nightly | |
| # regression must not be able to block a pull request. It runs on pushes that | |
| # touch the code, on main, and weekly, so a break is visible without being in | |
| # the way. | |
| # | |
| # Two known interactions, both handled rather than papered over: | |
| # * the sanitizer runtime defines its own `mlock`, so the RLIMIT_MEMLOCK | |
| # refusal test cannot reach the kernel. That test detects the interception | |
| # and reports which assertion it skipped. | |
| # * `pamwire`'s wiring tests leak a `&'static str` on purpose; the scope and | |
| # the reasoning are in .github/lsan-suppressions.txt. | |
| name: ASan | |
| # Push-triggered on every branch, so a same-repo pull request picks this up | |
| # through its own branch. A fork PR fires no push event here and therefore gets | |
| # no ASan run, which is acceptable precisely because this is not a required | |
| # check. | |
| on: | |
| push: | |
| branches: ["**"] | |
| paths: | |
| - "crates/**" | |
| - "Cargo.toml" | |
| - "Cargo.lock" | |
| - ".github/workflows/asan.yml" | |
| - ".github/lsan-suppressions.txt" | |
| - "scripts/ci-bubblewrap.sh" | |
| - "scripts/fetch-ort.sh" | |
| schedule: | |
| - cron: "40 4 * * 3" # Wednesdays 04:40 UTC, clear of the other scheduled jobs | |
| workflow_dispatch: | |
| # Rapid pushes to a branch should not stack ASan runs; only the newest commit is | |
| # worth sanitizing, and this job is one of the slowest in the repo. Cancelling is | |
| # safe because ASan is not a required check, so a superseded run going away | |
| # cannot block a merge. | |
| # | |
| # The schedule gets its OWN group, and that separation is the load-bearing part. | |
| # A scheduled run fires on the default branch, so keying the group on the ref | |
| # alone would put the weekly run in `asan-refs/heads/main` alongside ordinary | |
| # main pushes. A concurrency group holds exactly one PENDING run, and a newly | |
| # queued run "is canceled and replaced" per GitHub's concurrency documentation. | |
| # `cancel-in-progress: false` does not save it: that protects the run already | |
| # executing, not the one waiting behind it. So a main push landing while the | |
| # weekly run sat pending would have silently deleted the weekly run, which is | |
| # the opposite of what this block exists to do. | |
| # | |
| # Cancelling stays off on main so an in-flight run there still finishes. | |
| concurrency: | |
| group: asan-${{ github.event_name == 'schedule' && 'weekly' || github.ref }} | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} | |
| permissions: | |
| contents: read | |
| jobs: | |
| asan: | |
| name: AddressSanitizer (test suite) | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 40 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| # Do not leave the job token in .git/config for later steps to reach. | |
| persist-credentials: false | |
| - name: Cache model weights | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: models | |
| key: models-${{ hashFiles('models/SHA256SUMS') }} | |
| - name: Fetch model weights (models-v1 release, sha256-verified) | |
| run: bash scripts/fetch-models.sh | |
| - name: Install system build dependencies | |
| run: | | |
| command -v apt-get >/dev/null || { echo "deps preinstalled on the self-hosted runner"; exit 0; } | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| build-essential pkg-config clang libclang-dev libpam0g-dev libtss2-dev libudev-dev libdbus-1-dev dbus-daemon llvm bubblewrap apparmor | |
| - name: Prepare and verify isolated CLI test namespaces | |
| run: bash scripts/ci-bubblewrap.sh | |
| # A leak suppression matches the SYMBOL NAMES in an allocation stack, so | |
| # without a symbolizer it matches nothing and the deliberate test leak | |
| # comes back as a failure with a stack of bare addresses. That is how this | |
| # job failed the first time it ran here, while passing locally where a | |
| # symbolizer happened to be installed. Resolve it explicitly and stop if it | |
| # is missing, rather than let the job degrade into an unreadable report. | |
| - name: Locate llvm-symbolizer (suppressions and reports both need it) | |
| run: | | |
| sym=$(command -v llvm-symbolizer || true) | |
| if [ -z "$sym" ]; then | |
| # Versioned installs land in /usr/lib/llvm-N/bin; take the newest. | |
| sym=$(find /usr/lib -maxdepth 3 -path '*/bin/llvm-symbolizer' -type f 2>/dev/null \ | |
| | sort -V | tail -1) | |
| fi | |
| if [ -z "$sym" ]; then | |
| echo "no llvm-symbolizer found; ASan reports would be bare addresses" >&2 | |
| exit 1 | |
| fi | |
| "$sym" --version | |
| echo "ASAN_SYMBOLIZER_PATH=$sym" >> "$GITHUB_ENV" | |
| - name: Install nightly Rust (-Zsanitizer is unstable) | |
| # @master (declares the `toolchain` input); the version tag branches | |
| # have no such input and ignore `with: toolchain:`. | |
| uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de # v1 | |
| with: | |
| toolchain: nightly | |
| # The sanitizer runtimes ship with the standard library for the | |
| # explicit target, which is also why --target is passed below: without | |
| # it, build scripts and proc macros would be instrumented too. | |
| components: rust-src | |
| targets: x86_64-unknown-linux-gnu | |
| - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 | |
| with: | |
| # Own cache key: an instrumented build shares nothing with the normal | |
| # one, and PRs restore without saving (see ci.yml for the quota). | |
| key: asan | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Fetch the ONNX runtime (ort loads it dynamically at runtime) | |
| run: | | |
| ver=1.28.1 | |
| bash scripts/fetch-ort.sh "$ver" | |
| echo "ORT_DYLIB_PATH=$PWD/onnxruntime-linux-x64-${ver}/lib/libonnxruntime.so" >> "$GITHUB_ENV" | |
| - name: test (instrumented with AddressSanitizer + LeakSanitizer) | |
| env: | |
| RUSTFLAGS: -Zsanitizer=address | |
| # A leak report names the allocation, not the leak's owner, so keep | |
| # the whole stack: the deliberate one is only distinguishable by the | |
| # test function that allocated it. | |
| ASAN_OPTIONS: detect_stack_use_after_return=1 | |
| run: | | |
| # print_suppressions puts "Suppressions used:" in the log, so a reader | |
| # can see the deliberate leak was matched rather than assume it. | |
| # Guarded because `cargo test` exits 0 even when it discovers no tests | |
| # at all. No cfg gates the test set by toolchain, so the nightly run | |
| # selects the same tests the stable lanes do. | |
| LSAN_OPTIONS="suppressions=$PWD/.github/lsan-suppressions.txt:print_suppressions=1" \ | |
| ./scripts/run-tests-guarded.sh --min 2720 -- \ | |
| cargo +nightly test --workspace --locked \ | |
| --target x86_64-unknown-linux-gnu |