open-standards/.github/workflows/wai-quantum-formats.yml
Transaction Science cb059d7a0b wai-quantum: other formats bounded against hostile input, in CI
A jeff register's size reached Vec::with_capacity before the qubit cap,
so one of 2^32 - 1 qubits asked for 34 GB: under wasm32-wasip2 the test
written first trapped in qureg_op. It is now refused (Q111) before
anything is made. The audit of every allocation and loop sized by the
input:

- the wire reader bounds every pointer, segment and list in 64 bits (a
  segment count of u32::MAX overflowed a 32-bit usize), and counts each
  list element against the words it may visit;
- the jeff reader counts every register slot, array element (an array
  copied by setIndex included), call frame and condition term against
  one budget, caps arrays, values per function, operations run and gates
  written, shares conditions between copies, and no longer truncates a
  64-bit size, index, selector or shift to fit;
- the QIR reader no longer copies a block's lines on each visit, bounds
  the nesting of branches, the instructions run over every path and the
  state each path copies, and dedups a condition's reads;
- a condition is at most 1 024 terms and 32 deep once written, a run of
  one operator written flat;
- a conversion's check is not run past 262 144 gates applied or 4e9
  amplitudes, and says so; both readers take modules of up to 16 MiB.

tests/formats_mutation.rs mutates every QIR and jeff file of the
conformance set from a fixed seed (45 153 inputs: bits flipped, files cut
short and spliced, every length, count, offset and 32-bit field inflated)
and holds each to converting or a refusal with a code of the table,
within 20 s and 1 GiB measured by a counting allocator.
wai-quantum-formats.yml runs it natively and under wasm32-wasip2 (a
release build with overflow checks), the count pinned.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-31 07:16:32 -04:00

174 lines
8.7 KiB
YAML

name: wai-quantum other formats
# The readers and writers of other formats (wai/wai-quantum/src/engine/formats/):
# QIR under its base and adaptive profiles and jeff through the crate's own
# Cap'n Proto reader and writer. Their unit tests (the wire format, the
# OpenQASM they write, every intrinsic and gate held to its matrix, each
# profile's refusals with their lines, the Pauli-product rotation against
# exp(iθP), every prefix of a module refused), and the conformance set
# (wai/quantum-conformance/formats/: the QIR specification's examples, the
# jeff project's benchmarks and examples, hand-built modules and OpenQASM
# sources written to each format, each with what the engine must do with it,
# the QIR written pinned by its BLAKE3), and hostile inputs: every file
# of the set mutated, each converted or refused with a code within its
# bounds, natively and under wasm32-wasip2.
#
# The set runs natively, where a second test holds its bundle equal to its
# files, and under wasm32-wasip2 by wasmtime, where the cases come from the
# bundle the test compiles in and the QIR written must be the same bytes.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [main]
paths:
- 'wai/wai-quantum/**'
- 'wai/quantum-conformance/formats/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-formats.yml'
pull_request:
paths:
- 'wai/wai-quantum/**'
- 'wai/quantum-conformance/formats/**'
- 'rust-toolchain.toml'
- '.github/workflows/wai-quantum-formats.yml'
jobs:
relevant:
name: does this change touch it
runs-on: linux-arm64
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-quantum-formats.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches the readers of other formats: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
native:
name: readers, writers and the set · linux
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: linux
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- name: Install Rust 1.98.0
shell: bash
run: |
export PATH="$HOME/.cargo/bin:$PATH"
command -v rustup >/dev/null 2>&1 || \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.98.0
rustup toolchain install 1.98.0 --profile minimal
rustup default 1.98.0
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
# A module filter is satisfied by any one test of the module, so each
# module's tests are required by name to have run.
- name: The readers' and writers' unit tests
shell: bash
working-directory: wai/wai-quantum
run: |
set -euo pipefail
cargo test --locked --release --features full --lib engine::formats 2>&1 | tee "$RUNNER_TEMP/formats.log"
grep -qE '^test result: ok\. [1-9][0-9]+ passed; 0 failed' "$RUNNER_TEMP/formats.log" || { echo "::error::the formats' tests did not pass"; exit 1; }
for m in engine::formats::capnp::tests engine::formats::emit::tests engine::formats::tests::every_gate_is_written_exactly engine::formats::tests::a_pauli_product_rotation_is_exp_i_theta_p engine::formats::tests::the_conformance_set_holds_these_modules engine::formats::tests::a_register_too_large_is_refused_before_it_is_allocated; do
grep -q "^test $m.* \.\.\. ok$" "$RUNNER_TEMP/formats.log" || { echo "::error::no test of $m ran"; exit 1; }
done
- name: The conformance set
shell: bash
working-directory: wai/wai-quantum
run: |
set -euo pipefail
cargo test --locked --release --features full --test formats_conformance 2>&1 | tee "$RUNNER_TEMP/set.log"
grep -q 'test result: ok. 2 passed; 0 failed' "$RUNNER_TEMP/set.log" || { echo "::error::the conformance set did not pass"; exit 1; }
# Hostile inputs: every file of the set mutated by a seeded generator
# (bits flipped, files cut short and spliced, and every length, count,
# offset and 32-bit field inflated), each converted or refused with a
# code within its time and memory bounds. The count of inputs is pinned.
- name: Hostile inputs
shell: bash
working-directory: wai/wai-quantum
run: |
set -euo pipefail
cargo test --locked --release --features full --test formats_mutation -- --nocapture 2>&1 | tee "$RUNNER_TEMP/mutation.log"
grep -q 'test result: ok. 1 passed; 0 failed' "$RUNNER_TEMP/mutation.log" || { echo "::error::a hostile input was not refused cleanly"; exit 1; }
grep -q '45153 inputs: ' "$RUNNER_TEMP/mutation.log" || { echo "::error::the count of hostile inputs is not the pinned 45153"; exit 1; }
- name: The readers without the receipts
shell: bash
working-directory: wai/wai-quantum
run: |
set -euo pipefail
cargo check --locked --no-default-features --features quantum_engine --lib
wasip2:
name: the set under wasm32-wasip2 (wasmtime)
needs: relevant
if: needs.relevant.outputs.run == 'true'
runs-on: linux
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- name: Install Rust 1.98.0 with the wasm32-wasip2 target
shell: bash
run: |
export PATH="$HOME/.cargo/bin:$PATH"
command -v rustup >/dev/null 2>&1 || \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.98.0
rustup toolchain install 1.98.0 --profile minimal
rustup default 1.98.0
rustup target add --toolchain 1.98.0 wasm32-wasip2
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
# The release binary, pinned by version and SHA-256, rather than a build from source.
- name: Install wasmtime 49.0.2
shell: bash
run: |
set -euo pipefail
[ "$(uname -m)" = x86_64 ] || { echo "::error::the pinned wasmtime archive is x86_64; this host is $(uname -m)"; exit 1; }
if ! command -v xz >/dev/null 2>&1; then
SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO="sudo"
$SUDO apt-get update -qq
$SUDO apt-get install -y -qq xz-utils
fi
v=49.0.2
f="wasmtime-v$v-x86_64-linux.tar.xz"
curl --proto '=https' --tlsv1.2 -sSfL -o "$RUNNER_TEMP/$f" \
"https://github.com/bytecodealliance/wasmtime/releases/download/v$v/$f"
echo "a4d6e9e3a5a60f527cf7793d674c48930c80c2e8977995b8a275cad3254b9322 $RUNNER_TEMP/$f" | sha256sum -c -
tar -xJf "$RUNNER_TEMP/$f" -C "$RUNNER_TEMP"
echo "$RUNNER_TEMP/wasmtime-v$v-x86_64-linux" >> "$GITHUB_PATH"
# A debug build, so integer overflow panics instead of wrapping. The test
# that reads the set's files has no wasip2 build; the one that runs the
# cases from the compiled-in bundle does.
- name: The conformance set under wasmtime
shell: bash
working-directory: wai/wai-quantum
env:
CARGO_TARGET_WASM32_WASIP2_RUNNER: wasmtime
run: |
set -euo pipefail
cargo test --locked --target wasm32-wasip2 --features full --test formats_conformance 2>&1 | tee "$RUNNER_TEMP/wasip2.log"
grep -q 'test result: ok. 1 passed; 0 failed' "$RUNNER_TEMP/wasip2.log" || { echo "::error::the conformance set did not pass under wasip2"; exit 1; }
# The same hostile inputs where an allocation larger than the address
# space panics: a release build with overflow checks on, as it is slow
# enough otherwise to outlast the job.
- name: Hostile inputs under wasmtime
shell: bash
working-directory: wai/wai-quantum
env:
CARGO_TARGET_WASM32_WASIP2_RUNNER: wasmtime
CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS: "true"
run: |
set -euo pipefail
cargo test --locked --release --target wasm32-wasip2 --features full --test formats_mutation -- --nocapture 2>&1 | tee "$RUNNER_TEMP/wasip2-mutation.log"
grep -q 'test result: ok. 1 passed; 0 failed' "$RUNNER_TEMP/wasip2-mutation.log" || { echo "::error::a hostile input was not refused cleanly under wasip2"; exit 1; }
grep -q '45153 inputs: ' "$RUNNER_TEMP/wasip2-mutation.log" || { echo "::error::the count of hostile inputs under wasip2 is not the pinned 45153"; exit 1; }