Repository navigation
145 lines (130 loc) · 5.77 KB
/
Copy pathdep_fuzzing.yml
File metadata and controls
145 lines (130 loc) · 5.77 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
name: Fuzzing Job
on:
workflow_call:
inputs:
max_total_time:
description: Maximum total time for the fuzz run in seconds
required: true
type: number
timeout_minutes:
description: Maximum total time for the fuzz job in minutes
required: true
type: number
persist_corpus:
description: Cache and minimise the corpus across runs. For long scheduled runs only.
required: false
type: boolean
default: false
target:
description: Fuzz targets to run
required: true
type: string
arch:
description: Architecture on which to fuzz
required: true
type: string
docs_only:
description: Skip fuzzing if docs only
required: false
type: string
default: "false"
prime_cache:
description: >-
Build every fuzz target from a cold target and save the cache, without
fuzzing. Skips the build when the cache is up to date.
required: false
type: boolean
default: false
permissions:
contents: read
jobs:
fuzz:
if: ${{ inputs.docs_only == 'false' }}
# Precomputed by callers because Actions expressions do not support arithmetic.
timeout-minutes: ${{ inputs.timeout_minutes }}
# Spread load across vendor pools: AMD when run_id ends in 0,2,4,6,8, Intel otherwise.
runs-on: ${{ fromJson(inputs.arch == 'X64'
&& format('["self-hosted", "Linux", "X64", "1ES.Pool=hld-kvm-{0}", "JobId=fuzz-{1}-{2}-{3}-{4}"]',
(endsWith(github.run_id, '0') || endsWith(github.run_id, '2') || endsWith(github.run_id, '4') || endsWith(github.run_id, '6') || endsWith(github.run_id, '8')) && 'amd' || 'intel',
inputs.target,
github.run_id,
github.run_number,
github.run_attempt)
|| '["self-hosted", "Linux", "arm64", "kvm"]') }}
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: hyperlight-dev/ci-setup-workflow@59673866360458f8dd035f4a9fa766aeebad2038 # v2.3.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# rust-cache hashes installed toolchains, so the pinned nightly must be
# installed before the key is computed.
- name: Install fuzzing toolchain
run: rustup toolchain install "$(just --evaluate nightly-toolchain)" --profile minimal
# One entry serves every target. Priming builds cold, so the entry holds
# only what this commit needs.
- name: Rust cache
id: rust-cache
uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3
with:
prefix-key: v1-rust
shared-key: "fuzz-${{ inputs.arch }}"
lookup-only: ${{ inputs.prime_cache }}
save-if: ${{ inputs.prime_cache }}
- name: Check whether the cache is up to date
id: prime
if: inputs.prime_cache
run: echo "skip=${{ steps.rust-cache.outputs.cache-hit == 'true' }}" >> "$GITHUB_OUTPUT"
- name: Download Rust guests
if: ${{ !inputs.prime_cache }}
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: rust-guests-${{ inputs.arch }}-release
path: src/tests/rust_guests/bin/release/
- name: Install cargo-fuzz
if: steps.prime.outputs.skip != 'true'
run: command -v cargo-fuzz >/dev/null 2>&1 || cargo install cargo-fuzz
- name: Build fuzz targets
if: inputs.prime_cache && steps.prime.outputs.skip != 'true'
run: just build-fuzz-targets
# cargo-fuzz resolves the fuzz project by walking up from the working
# directory, so the corpus lives at <repo root>/fuzz/corpus/<target>.
# Runners are single-use, so without this each run starts from an empty
# corpus and coverage-guided fuzzing never accumulates progress.
- name: Restore fuzz corpus
if: ${{ inputs.persist_corpus }}
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: fuzz/corpus/${{ inputs.target }}
key: fuzz-corpus-${{ inputs.target }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
fuzz-corpus-${{ inputs.target }}-
- name: Run Fuzzing
if: ${{ !inputs.prime_cache }}
run: just fuzz-timed ${{ inputs.target }} ${{ inputs.max_total_time }} > /dev/null
working-directory: src/hyperlight_host
# The corpus grows every week and the repo shares a 10 GB cache budget
# with the rust-cache entries PrimeCaches.yml maintains. cmin keeps only
# the minimal input set preserving coverage, bounding that growth.
# Housekeeping, so a failure here must not fail the job.
- name: Minimise fuzz corpus
if: ${{ always() && inputs.persist_corpus }}
timeout-minutes: 20
continue-on-error: true
run: just fuzz-cmin ${{ inputs.target }}
working-directory: src/hyperlight_host
# always() so the evolved corpus survives a timeout or a crash, not just
# a clean run.
- name: Save fuzz corpus
if: ${{ always() && inputs.persist_corpus }}
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: fuzz/corpus/${{ inputs.target }}
key: fuzz-corpus-${{ inputs.target }}-${{ github.run_id }}-${{ github.run_attempt }}
- name: Upload Crash Artifacts
if: failure() # This ensures artifacts are only uploaded on failure
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: fuzz-crash-artifacts-${{ inputs.target }}
path: fuzz/artifacts/
retention-days: 28