Skip to content

Introduce OCI image unpack feature #443

Introduce OCI image unpack feature

Introduce OCI image unpack feature #443

Workflow file for this run

# Static analyzers over the macOS Apple Silicon build.
#
# tidy-macos : clang-tidy via `make lint`
# scan-macos : LLVM scan-build via `make analyze`
# infer-macos : Facebook Infer capture + analyze over the full build
name: Static analysis
# All three jobs analyze "make elfuse" and nothing else, so tests/ joins the
# prose and configuration below: a change there cannot alter a single
# translation unit any of them sees. paths-ignore skips the run only when EVERY
# changed file matches, and anything not listed still runs the full set, which
# is the safe direction for a path that turns out to matter after all.
#
# On pull_request only, and the same conditions build.yml states at length: one
# list per file so nothing can drift, and a path filter skips the whole run, so
# a REQUIRED check that never reports would stay pending. main has no branch
# protection today; making these required means moving the filter into the
# jobs.
on:
push:
branches: [main]
pull_request:
branches: [main]
paths-ignore:
- '**.md'
- 'docs/**'
- 'LICENSE'
- 'tests/**'
- '.agents/**'
- '.claude/**'
- '.editorconfig'
- '.clang-format'
- 'frama-c-stubs/**'
- '.github/workflows/build.yml'
- '.github/workflows/lint.yml'
- '.github/workflows/verify.yml'
workflow_dispatch:
# A merge queue runs the merged result, not the PR, and merge_group carries
# no path filter and no pull_request payload: every check runs in full there.
# Without this trigger a queue would merge with nothing having run at all.
merge_group:
# Cancel in-progress runs for the same PR; keep main runs going.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
jobs:
# clang-tidy via `make lint`. Runs in parallel with build/scan jobs.
# Mostly advisory: findings are logged for review. The exception is
# readability-function-size, which .clang-tidy names in WarningsAsErrors,
# so a function crossing the 400-line ceiling fails this job.
tidy-macos:
name: clang-tidy (macOS Apple Silicon)
runs-on: macos-15
timeout-minutes: 20
env:
GNU_OBJCOPY: /opt/homebrew/opt/binutils/bin/objcopy
HOMEBREW_NO_INSTALL_CLEANUP: 1
HOMEBREW_NO_AUTO_UPDATE: 1
# binutils is needed because make lint depends on the shim_blob.h
# generated by the assembly + objcopy pipeline.
BREW_PKGS: binutils llvm
CLANG_TIDY: /opt/homebrew/opt/llvm/bin/clang-tidy
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Cache Homebrew downloads
uses: actions/cache@v6
with:
path: ~/Library/Caches/Homebrew/downloads
key: brew-${{ runner.os }}-${{ runner.arch }}-${{ env.BREW_PKGS }}
- name: Install Homebrew packages
# shellcheck disable=SC2086 -- BREW_PKGS is a space-separated list.
run: |
set -euo pipefail
brew install --quiet $BREW_PKGS
"$CLANG_TIDY" --version | head -1
- name: clang-tidy (make lint)
run: make lint
# LLVM scan-build via `make analyze`. Runs in parallel with build/tidy.
# Advisory: scan-build's Make target does not pass --status-bugs, so
# findings appear in logs and in the uploaded HTML report but do not
# gate the job.
scan-macos:
name: scan-build (macOS Apple Silicon)
runs-on: macos-15
timeout-minutes: 25
env:
GNU_OBJCOPY: /opt/homebrew/opt/binutils/bin/objcopy
HOMEBREW_NO_INSTALL_CLEANUP: 1
HOMEBREW_NO_AUTO_UPDATE: 1
BREW_PKGS: binutils llvm
LLVM_BIN: /opt/homebrew/opt/llvm/bin
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Cache Homebrew downloads
uses: actions/cache@v6
with:
path: ~/Library/Caches/Homebrew/downloads
key: brew-${{ runner.os }}-${{ runner.arch }}-${{ env.BREW_PKGS }}
- name: Install Homebrew packages
# shellcheck disable=SC2086 -- BREW_PKGS is a space-separated list.
# scan-build has no --version; piping --help into `head -1` makes
# perl take SIGPIPE on the closed stdout and exit non-zero, which
# under pipefail fails the step. Just confirm the binary exists.
run: |
set -euo pipefail
brew install --quiet $BREW_PKGS
test -x "$LLVM_BIN/scan-build"
"$LLVM_BIN/clang" --version | head -1
- name: scan-build (make analyze)
run: |
set -euo pipefail
export PATH="$LLVM_BIN:$PATH"
mkdir -p build/scan-build
scan-build -o build/scan-build --use-cc="$(command -v clang)" \
make -B elfuse
- name: Upload scan-build report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: scan-build-${{ runner.os }}-${{ runner.arch }}
path: build/scan-build
retention-days: 7
if-no-files-found: ignore
# Facebook Infer over the full elfuse build. Must run on macOS Apple
# Silicon because the build needs Hypervisor.framework and -arch arm64;
# Infer captures the real clang invocations, so it sees every TU.
#
# Gating (unlike tidy-macos/scan-macos): a separate step turns Infer's
# report.json into inline ::error:: annotations plus a job summary, then
# fails the job. Findings surface on the PR instead of a silent exit code,
# so bugs are pruned at PR time instead of merged.
infer-macos:
name: Infer (macOS Apple Silicon)
runs-on: macos-15
timeout-minutes: 25
env:
GNU_OBJCOPY: /opt/homebrew/opt/binutils/bin/objcopy
HOMEBREW_NO_INSTALL_CLEANUP: 1
HOMEBREW_NO_AUTO_UPDATE: 1
BREW_PKGS: binutils
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Cache Homebrew downloads
uses: actions/cache@v6
with:
path: ~/Library/Caches/Homebrew/downloads
key: brew-${{ runner.os }}-${{ runner.arch }}-${{ env.BREW_PKGS }}
- name: Install GNU objcopy
# shellcheck disable=SC2086 -- BREW_PKGS is a space-separated list.
run: |
set -euo pipefail
brew install --quiet $BREW_PKGS
"$GNU_OBJCOPY" --version | head -1
- name: Setup Infer
# infer_version pins the Infer binary; the action itself tracks the v1
# tag.
uses: srz-zumix/setup-infer@v1
with:
infer_version: v1.3.0
# .inferconfig disables PULSE_UNINITIALIZED_VALUE repo-wide. Pulse cannot
# prove guest_copy's chunked "while (copied < len)" loop fills its
# destination, so every guest_read_small caller looks uninitialized; the
# findings were audited and every caller checks the return value. The rest
# of the Infer gate is untouched: null dereference, use-after-free, leaks,
# dead stores and stack-address escape all still fail the job.
#
# The cost is real and repo-wide: a genuinely uninitialized read added
# after this point is not caught here. Scoping it narrower was tried and
# is worse -- the findings span thirteen files including syscall.c and
# proc.c, so a path block list suppresses the same class over most of the
# syscall surface while being harder to read, and censor-report does not
# take effect through `infer run` in v1.3.0. `make infer-uninit` re-runs
# the analysis with the checker back on and prints the count, so whether
# an Infer upgrade has made this unnecessary is one command away.
- name: Infer capture + analyze (make -B elfuse)
# -B forces a clean rebuild so Infer captures every translation unit.
# Non-C build steps (shim.S assembly, objcopy) pass through untouched.
# No --fail-on-issue here: `infer run` must exit 0 so the reporting
# step below runs and surfaces findings before the job fails.
# --keep-going tolerates a frontend failure on an odd TU, but that can
# also mask a total capture miss (wrapper never intercepts clang, 0
# files analyzed). The count guard fails the job on that silent no-op.
run: |
set -euo pipefail
infer run --keep-going -- make -B elfuse 2>&1 | tee infer-run.log
n=$(grep -oE 'Found [0-9]+ source file' infer-run.log \
| grep -oE '[0-9]+' | tail -1 || true)
echo "Infer captured ${n:-0} source files"
test "${n:-0}" -gt 0
- name: Report Infer findings
# Emit GitHub annotations + a job summary from report.json, then exit
# non-zero if any finding exists. Runs even when a prior step failed so
# a partial report is still surfaced.
if: ${{ !cancelled() }}
run: python3 scripts/infer-annotate.py infer-out/report.json
- name: Upload Infer report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: infer-${{ runner.os }}-${{ runner.arch }}
path: infer-out/report.txt
retention-days: 7
if-no-files-found: warn