Repository navigation
366 lines (353 loc) · 16.8 KB
/
Copy pathci.yml
File metadata and controls
366 lines (353 loc) · 16.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
name: ci
on:
push:
branches: [main]
# A release tag runs the Kani proofs (see the `proofs` job). v1.1.0 shipped
# with a proof that had been broken by an unrelated optimisation, because
# nothing verified them at release time and the weekly cron below had never
# actually fired.
tags: ["v*"]
pull_request:
# Weekly advisory sweep (H-40): cargo-audit re-checks the RustSec feed even
# when nothing is being merged. The same trigger runs the Kani proofs, which
# are too slow to block every PR.
schedule:
- cron: "0 6 * * 1"
workflow_dispatch:
# Least-privilege token (H-37): nothing in this workflow writes to the repo.
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
# Actions are SHA-pinned (H-37): a moved tag must not change what CI runs.
# checkout 11d5960a = v4.2.2 · rust-toolchain 6c977a6c = master 2026-07 ·
# setup-node 49933ea5 = v4.4.0. Bump deliberately, with the SHA.
jobs:
test:
strategy:
fail-fast: false
matrix:
# macOS since 2.2.2: through 2.2.1 the crate did not build there at
# all (a `mincore` out-vector that is `c_uchar` on Linux and `c_char`
# on Apple), and nothing tested it.
os: [windows-latest, ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772
with:
toolchain: 1.97.1
components: rustfmt, clippy
- name: fmt
run: cargo fmt --all --check
- name: clippy (all features)
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
- name: check (no features)
run: cargo check --workspace --all-targets
# Each optional feature ALONE. `--all-features` and the default are the
# only two points the steps above build, and a feature that only
# compiles beside another rots unseen: `linkcheck` without `secure` had
# a call with one argument too many for months (2026-09-09).
- name: clippy (each feature alone)
shell: bash
run: |
set -euo pipefail
for f in secure linkcheck blockmap debug_checks; do
echo "::group::--features $f"
cargo clippy -p rusty_alloc --all-targets --features "$f" -- -D warnings
echo "::endgroup::"
done
- name: test (all features)
run: cargo test --workspace --all-features
# H-06/H-08/H-09: the supply-chain gate — policy (deny) + advisories (audit)
# on every PR, tools version-pinned so the gate itself is reproducible.
supply-chain:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772
with:
toolchain: 1.97.1
- name: install cargo-deny + cargo-audit + cargo-vet (pinned)
run: |
cargo install cargo-deny --version 0.20.2 --locked
cargo install cargo-audit --version 0.22.2 --locked
cargo install cargo-vet --version 0.10.2 --locked
- name: cargo deny check
run: cargo deny check
- name: cargo audit
run: cargo audit --deny warnings
# H-10: every SHIPPING dependency carries an audit certification; the
# remaining exemptions are dev-only and pinned at safe-to-run, so this
# fails the moment one of them becomes a runtime dependency.
- name: cargo vet
run: cargo vet --locked
# H-22: static analysis BEYOND clippy. The rules in tools/semgrep-rules.yml
# are written from this crate's own incident history (docs/LEDGER.md) — a
# generic ruleset would find none of them. Their first run found a real
# latent defect: `remove_huge_segment` still had the release-compiled-out
# `debug_assert!(false, ..)` shape that caused the 0.4.0 dangling-list
# crash on the normal-segment path.
static-analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: install semgrep (pinned)
run: pipx install semgrep==1.173.0 || pip install --user semgrep==1.173.0
- name: semgrep (repo-specific rules)
run: semgrep --config tools/semgrep-rules.yml --error --quiet
# The rules must be able to FAIL, or they are decoration. This asserts
# they still fire on the bad shapes they exist to catch.
- name: prove the rules are not vacuous
run: bash tools/semgrep-selftest.sh
# H-11: the unsafe ratchet. Growth is allowed but must be deliberate —
# documented in UNSAFE.md and re-baselined in the same commit.
- name: unsafe census ratchet
run: bash tools/unsafe-census.sh
# The same argument as `prove the rules are not vacuous`, applied to the
# TESTS: reintroduce each defect a load-bearing test guards and require the
# suite to go red. Four tests in this repo have passed under the exact bug
# they existed to catch; this is what stops the fifth.
- uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772
with:
toolchain: 1.97.1
- name: prove the gates are not vacuous
run: bash tools/gate-selftest.sh
# H-30: the proofs must keep proving. Kani is slow, so this runs on a
# schedule and on demand rather than blocking every PR — the harnesses
# cover arithmetic that changes rarely, and a regression there would also
# trip the property tests and Miri long before a release.
proofs:
if: >-
github.event_name == 'schedule' ||
github.event_name == 'workflow_dispatch' ||
startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772
with:
toolchain: 1.97.1
- name: install kani (pinned)
run: cargo install --locked kani-verifier --version 0.67.0 && cargo kani setup
# --ignore-global-asm: Kani cannot analyse the crate's `global_asm!`
# (the initial-exec TLS slot). None of these harnesses touch that code —
# they prove pure index/size arithmetic — so the exclusion does not
# weaken them, but it is stated rather than hidden.
- name: cargo kani
run: cargo kani -p rusty_alloc -Z unstable-options --ignore-global-asm
# H-26/H-27: the fuzz targets must BUILD and survive a short libFuzzer run
# on every PR (the long soak runs on its own schedule; this catches a
# target that rotted or a crasher that reproduces instantly).
fuzz-smoke:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772
with:
toolchain: nightly
- name: install cargo-fuzz (pinned)
run: cargo install cargo-fuzz --version 0.13.2 --locked
- name: fuzz alloc_ops (smoke)
run: cargo +nightly fuzz run alloc_ops -- -max_total_time=60
- name: fuzz xthread (smoke)
run: cargo +nightly fuzz run xthread -- -max_total_time=60
# The compile-gate rule: both tier-1 targets checked even where they don't run.
cross-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772
with:
toolchain: 1.97.1
targets: x86_64-pc-windows-msvc
- name: check windows target from linux
run: cargo check --workspace --target x86_64-pc-windows-msvc
# The EMBEDDED surface: `no_std`, the `ra_small_profile` geometry, and a
# bare-metal target with no 64-bit atomics.
#
# Everything the small-metal campaign added — the fixed-region prim backend,
# the second geometry, the `split64` shim, the reclamation fixes — was
# verified by hand and by NOTHING ELSE until this job existed. Five real
# defects were found in that surface in a single session; every one of them
# would have passed the jobs above, because none of them build `no_std`, none
# set `ra_small_profile`, and none target a chip.
#
# riscv32imac/imafc rather than Xtensa: they are stock rustup targets, and
# they exercise the properties that matter — `no_std`, a 32-bit `usize`, and
# `cfg(not(target_has_atomic = "64"))`, which is what selects `split64` over
# `portable-atomic`. The ESP32-S3 board runs are in
# `docs/plans/small-metal.md`; they need hardware and cannot gate a PR.
embedded:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772
with:
toolchain: 1.97.1
components: clippy
targets: riscv32imac-unknown-none-elf, riscv32imafc-unknown-none-elf
# The second geometry is a full test run, not a build check: it changes
# segment/slice/page arithmetic that most of the suite depends on.
- name: test (small profile)
run: cargo test -p rusty_alloc
env:
RUSTFLAGS: --cfg ra_small_profile
- name: clippy (small profile)
run: cargo clippy -p rusty_alloc --all-targets -- -D warnings
env:
RUSTFLAGS: --cfg ra_small_profile
# `ra_aligned_region` restores the hosted segment mask on a fixed region
# (docs/plans/finished/region-alignment-dissolve.md). The backend's
# sizing, its placement and `Region`'s alignment all follow the flag, so
# that arm is tested, not just built.
- name: test + clippy (aligned-region knob)
run: |
cargo test -p rusty_alloc --lib prim::fixed
cargo test -p rusty_alloc --test region
cargo clippy -p rusty_alloc --all-targets -- -D warnings
env:
RUSTFLAGS: --cfg ra_small_profile --cfg ra_single_threaded --cfg ra_aligned_region
# `ra_segment_size="256k"` is the geometry a large-allocation consumer
# runs (docs/plans/finished/esp32-large-alloc-ceiling.md). It moves
# SEGMENT_SIZE, LARGE_OBJ_SIZE_MAX and every sizing rule, so the WHOLE
# suite runs there -- a geometry that only builds is a geometry that rots.
- name: test + clippy (256k segment geometry)
run: |
cargo test -p rusty_alloc
cargo clippy -p rusty_alloc --all-targets -- -D warnings
env:
RUSTFLAGS: --cfg ra_small_profile --cfg ra_segment_size="256k"
# The no_std build REFUSES to compile without `ra_single_threaded`: its
# soundness rests on there being exactly one thread, and that has to be
# opted into rather than inherited. The gate's negative case is asserted
# below, in the same spirit as `prove the rules are not vacuous`.
- name: clippy (no_std)
run: cargo clippy -p rusty_alloc --no-default-features -- -D warnings
env:
RUSTFLAGS: --cfg ra_single_threaded
# The reentrancy detector only exists under `ra_single_threaded`, so the
# normal test run cannot see it. An allocating ISR is the one bare-metal
# failure that used to be an unbounded spin with no message.
- name: reentrancy detector (single-context builds)
run: cargo test -p rusty_alloc --lib prim::fixed
env:
RUSTFLAGS: --cfg ra_single_threaded
- name: prove the single-thread gate is not vacuous
run: |
if cargo check -p rusty_alloc --no-default-features \
--target riscv32imac-unknown-none-elf 2>/dev/null; then
echo "no_std built WITHOUT --cfg ra_single_threaded; the gate is gone" >&2
exit 1
fi
echo "gate fires: no_std refuses to build without the opt-in"
# BOTH geometries on BOTH bare-metal targets. The matrix is the point:
# `ra_small_profile` and `no_std` are independent axes, and a defect that
# needs both is exactly the kind this job exists to catch.
- name: build no_std bare metal (both targets, both geometries)
run: |
set -euo pipefail
for target in riscv32imac-unknown-none-elf riscv32imafc-unknown-none-elf; do
for flags in "" "--cfg ra_small_profile"; do
echo "::group::$target ${flags:-default geometry}"
RUSTFLAGS="--cfg ra_single_threaded $flags" cargo build -p rusty_alloc \
--no-default-features --target "$target"
echo "::endgroup::"
done
done
# `rusty_alloc-api` is what a firmware actually depends on, so its
# `no_std` path is gated too rather than assumed from the core crate.
- name: build rusty_alloc-api no_std
run: cargo build -p rusty_alloc-api --no-default-features --target riscv32imac-unknown-none-elf
env:
RUSTFLAGS: --cfg ra_small_profile --cfg ra_single_threaded
# The README quotes instruction-count ratios against mimalloc, jemalloc and
# glibc. `bench/icount-arms.sh` is what produces every column of them, but
# nothing RE-RAN it, so the published figures aged silently: P4e's reclamation
# fixes cost 2.2-7.9 % on hardware and the host table still predated them.
#
# Scheduled rather than per-PR because callgrind is slow — the same reasoning
# as `proofs`. It uploads the table so a release can be cut against a number
# that was measured rather than remembered.
icount:
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772
with:
toolchain: 1.97.1
- name: install valgrind and jemalloc
run: |
sudo apt-get update
sudo apt-get install -y valgrind libjemalloc2
- name: build oracle arms
run: bash oracle/build.sh
- name: build the override shim
run: cargo build --release -p rusty_alloc-override
- name: instructions retired, all arms
run: |
set -o pipefail
RA_OVERRIDE_LIB="$PWD/target/release/librusty_alloc_override.so" \
bash bench/icount-arms.sh 2>&1 | tee icount.txt
- name: publish the table
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: icount-${{ github.sha }}
path: icount.txt
# G4: the os-layer logic must stay UB-free under miri against the mock prim.
miri:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772
with:
toolchain: nightly
components: miri
- name: miri (mock prim backend)
run: cargo +nightly miri test -p rusty_alloc
# wasm is a shipped target, so it gets a gate that EXECUTES rather than one
# that only compiles: `cargo test` cannot run wasm32-unknown-unknown, so the
# self-test is instantiated in a real VM under node.
wasm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772
with:
toolchain: 1.97.1
targets: wasm32-unknown-unknown
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "20"
- name: check core for wasm32
run: cargo check -p rusty_alloc --target wasm32-unknown-unknown
- name: build wasm self-test
run: cargo build -p rusty_alloc-wasm --target wasm32-unknown-unknown --release
- name: run self-test inside a WebAssembly VM
run: node bench/wasm-selftest.mjs target/wasm32-unknown-unknown/release/rusty_alloc_wasm.wasm
# SIZE is a shipped property of a wasm allocator: every byte is downloaded
# by every visitor to every page using it. Nothing measured it, so a
# 3,700-byte-gzipped regression sat in the crate for its whole life until
# an integrator reported it. See docs/plans/wasm-size.md.
- name: wasm size ratchet
run: bash tools/wasm-size.sh
oracle:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
submodules: recursive
- name: build oracle arms
run: bash oracle/build.sh
# OS-NAMESPACED output: oracle/build.sh writes to out/<uname>/<arm> because
# this repo is shared between Windows and WSL and a mixed-OS cmake cache in
# one directory corrupts both builds.
- name: oracle version gate
run: |
set -e
out="oracle/out/$(uname -s | tr '[:upper:]' '[:lower:]')/mi"
ls -la "$out"
test -e "$out/libmimalloc.so"