microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
joaoboechat/test-get-azdo-userid

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

.github/workflows/fuzz.yml

270lines · modecode

1name: fuzz
2run-name: Fuzz
3env:
4 OWNER_RDPATH: ./source # Rel path to the dir that contains the fuzzing infra (contains "fuzz" dir).
5 DURATION_SEC: 7200 # Fuzzing run duration in seconds.
6 STDERR_LOG_FNAME: fuzz.stderr.log # File name to redirect the fuzzing run's stderr to.
7 TMIN_LOG_FNAME: fuzz.tmin.log # File name to redirect the fuzzing input minimization log to.
8 ARTIFACTS_RDPATH: fuzz/artifacts # Fuzzing artifacts rel dir path.
9 SEEDS_RDPATH: fuzz/seed_inputs # Fuzzing seed inputs rel dir path.
10 SEEDS_FNAME: list.txt # Fuzzing seed inputs list file name.
11 PYTHON_VERSION: "3.11"
12
13on:
14 workflow_dispatch: # Manual runs.
15 push:
16 branches:
17 - main # Development runs against main branch.
18 paths:
19 - "source/compiler/**" # Run if the compiler was changed.
20 - "source/fuzz/**" # Run if the fuzzing infra was changed.
21 - ".github/workflows/fuzz.yml" # Run if the workflow itself was changed.
22 - "!source/compiler/qsc_eval/**" # Exclude the qsc_eval dir.
23 - "!source/compiler/qsc_codegen/**" # Exclude the qsc_codegen dir.
24
25jobs:
26 fuzz:
27 name: Fuzzing
28 strategy:
29 fail-fast: false
30 matrix:
31 os:
32 [ubuntu-latest] # Fuzzing is not supported on Win. The macos is temporarily removed
33 # because of low availability.
34 target_name: [qsharp, qasm]
35
36 runs-on: ${{ matrix.os }}
37 permissions:
38 issues: write
39 steps:
40 - name: Install and Configure Tools
41 run: |
42 rustup install nightly # Install nightly toolchain.
43 rustup default nightly # Make nightly toolchain default.
44 cargo install cargo-fuzz # Install cargo-fuzz (fuzzing tool).
45
46 - name: Checkout the Repo
47 uses: actions/checkout@v6
48 with:
49 submodules: "true"
50 - uses: actions/setup-python@v6
51 with:
52 python-version: ${{ env.PYTHON_VERSION }}
53
54 - name: Gather the Seed Inputs
55 if: matrix.target_name == 'qsharp'
56 working-directory: ${{ env.OWNER_RDPATH }}
57 run: |
58 # Clone the submodules of QDK:
59 REPOS="Quantum Quantum-NC QuantumKatas QuantumLibraries iqsharp qdk-python qsharp-compiler qsharp-runtime"
60 for REPO in $REPOS ; do
61 git clone --depth 1 --single-branch --no-tags --recurse-submodules --shallow-submodules --jobs 4 \
62 https://github.com/microsoft/$REPO.git $SEEDS_RDPATH/${{ matrix.target_name }}/$REPO
63 done
64
65 # Build a comma-separated list of all the .qs files in $SEEDS_FNAME file:
66 find $SEEDS_RDPATH/${{ matrix.target_name }} -name "*.qs" | tr "\n" "," > \
67 $SEEDS_RDPATH/${{ matrix.target_name }}/$SEEDS_FNAME
68
69 - name: Gather the Seed Inputs (qasm)
70 if: matrix.target_name == 'qasm'
71 working-directory: ${{ env.OWNER_RDPATH }}
72 run: |
73 # Clone openqasm repo for samples:
74 git clone --depth 1 --single-branch --no-tags --recurse-submodules --shallow-submodules --jobs 4 \
75 https://github.com/openqasm/openqasm.git $SEEDS_RDPATH/${{ matrix.target_name }}/openqasm
76
77
78 # Build a comma-separated list of all the .qasm and .inc files in $SEEDS_FNAME file:
79 find $SEEDS_RDPATH/${{ matrix.target_name }} -name "*.qasm" | tr "\n" "," > \
80 $SEEDS_RDPATH/${{ matrix.target_name }}/$SEEDS_FNAME
81 find $SEEDS_RDPATH/${{ matrix.target_name }} -name "*.inc" | tr "\n" "," > \
82 $SEEDS_RDPATH/${{ matrix.target_name }}/$SEEDS_FNAME
83
84 - name: Build and Run the Fuzz Target
85 working-directory: ${{ env.OWNER_RDPATH }}
86 run: |
87 cargo fuzz build --fuzz-dir ./fuzz --release --sanitizer=none --features do_fuzz ${{ matrix.target_name }} # Build the fuzz target.
88
89 # Run fuzzing for specified number of seconds and redirect the `stderr` to a file
90 # whose name is specified by the STDERR_LOG_FNAME env var:
91 RUST_BACKTRACE=1 cargo fuzz run --fuzz-dir ./fuzz --release --sanitizer=none --features do_fuzz ${{ matrix.target_name }} -- \
92 -seed_inputs=@$SEEDS_RDPATH/${{ matrix.target_name }}/$SEEDS_FNAME \
93 -max_total_time=$DURATION_SEC \
94 -rss_limit_mb=4096 \
95 -max_len=20000 \
96 2>$STDERR_LOG_FNAME
97 # The `-rss_limit_mb` and `-max_len` work around running out of memory.
98
99 - name: "If Fuzzing Failed: Collect Failure Info"
100 if: failure()
101 working-directory: ${{ env.OWNER_RDPATH }}
102 run: |
103 # Extract from stderr log the panic message:
104 PANIC_MESSAGE=`cat $STDERR_LOG_FNAME |
105 grep "panicked at" | sed "s|thread '<unnamed>' panicked at '\([^']*\).*|\1|"`
106 # Explanation:
107 # `cat $STDERR_LOG_FNAME |`: Display the contents of the stderr log file and pass the contents
108 # to the next command.
109 # `grep "panicked at" |`: Filter out (drop) all the lines except the ones containing "panicked at",
110 # the script expects that there is only one such line, pass that line to the next command. Line example:
111 # thread '<unnamed>' panicked at 'global item should have type', . . ./compiler/qsc_frontend/src/typeck/rules.rs:300:26
112 # `sed "s|thread '<unnamed>' panicked at '\([^']*\).*|\1|"`: `sed` - stream editor.
113 # `s` after quote: search command. After `s` there are two sections, each between a pair of '|'.
114 # First section:
115 # In the incoming stream search for a sequence starting with "thread '<unnamed>' panicked at '"
116 # (sequence from the beginning of the line until after the apostrophe where the panic message starts),
117 # followed by zero or more ('*' after ']') non-apostrophe chars (`[^']`)
118 # and memorize ( `\(`, `\)` ) that sequence of non-apostrophe chars (between apostrophes -
119 # "global item should have type") as a memory item 1;
120 # followed by zero or more ('*' after '.') arbitrary chars ('.') till the end of the line.
121 # Second section (`\1`):
122 # If the sequence specified by the first section is found, then replace that sequence (the whole line)
123 # with the memory item 1 (`\1`), ending up in a panic message between the apostrophes.
124 # PANIC_MESSAGE=`. . .`: The output of the command(s) between the backticks ('`') is saved in the
125 # env var PANIC_MESSAGE.
126 # If the failure is not panic-based then extract any ERROR message(s):
127 if [ "$PANIC_MESSAGE" == "" ]; then
128 PANIC_MESSAGE=`cat $STDERR_LOG_FNAME | grep "ERROR"`
129 fi
130 echo "PANIC_MESSAGE: '$PANIC_MESSAGE'" # Output the PANIC_MESSAGE var value to the log
131 # (optional, for workflow failure analysis and sanity check).
132 echo "PANIC_MESSAGE=$PANIC_MESSAGE" >> "$GITHUB_ENV" # Save the PANIC_MESSAGE var in the env, will be used in
133 # the subsequent `run:` and `uses:` steps.
134
135 # Determine the name of a file containing the input of interest (that triggers the panic/crash):
136 if [ -e $ARTIFACTS_RDPATH/${{ matrix.target_name }}/crash-* ]; then # Panic and Stack Overflow Cases.
137 TO_MINIMIZE_FNAME=crash-*;
138 elif [ -e $ARTIFACTS_RDPATH/${{ matrix.target_name }}/oom-* ]; then # Out-of-Memory Case.
139 TO_MINIMIZE_FNAME=oom-*;
140 else
141 echo -e "File to minimize not found.\nContents of artifacts dir \"$ARTIFACTS_RDPATH/${{ matrix.target_name }}/\":"
142 ls $ARTIFACTS_RDPATH/${{ matrix.target_name }}/
143 fi
144
145 if [ "$TO_MINIMIZE_FNAME" != "" ]; then
146 echo "TO_MINIMIZE_FNAME: $TO_MINIMIZE_FNAME"
147
148 # Minimize the input:
149 ( cargo fuzz --fuzz-dir ./fuzz tmin --release --sanitizer=none --features do_fuzz -r 10000 ${{ matrix.target_name }} $ARTIFACTS_RDPATH/${{ matrix.target_name }}/$TO_MINIMIZE_FNAME 2>&1 ) > \
150 $TMIN_LOG_FNAME || MINIMIZATION_FAILED=1
151
152 # Get the minimized input relative faile path:
153 if [ "$MINIMIZATION_FAILED" == "1" ]; then
154 # Minimization failed, get the latest successful minimized input relative faile path:
155 MINIMIZED_INPUT_RFPATH=`
156 cat $TMIN_LOG_FNAME | grep "CRASH_MIN: minimizing crash input: " | tail -n 1 |
157 sed "s|^.*\($ARTIFACTS_RDPATH/${{ matrix.target_name }}/[^\']*\).*|\1|"`
158 else
159 # Minimization Succeeded, get the reported minimized input relative faile path::
160 MINIMIZED_INPUT_RFPATH=`
161 cat $TMIN_LOG_FNAME | grep "failed to minimize beyond" |
162 sed "s|.*\($ARTIFACTS_RDPATH/${{ matrix.target_name }}/[^ ]*\).*|\1|" `
163 fi
164 echo "MINIMIZED_INPUT_RFPATH: $MINIMIZED_INPUT_RFPATH"
165 echo "MINIMIZED_INPUT_RFPATH=$MINIMIZED_INPUT_RFPATH" >> "$GITHUB_ENV"
166
167 # Extract the minimized input:
168 MINIMIZED_INPUT=`cat $MINIMIZED_INPUT_RFPATH | tr "\n" "\r"`
169 # Display the contents of the minimized input file and replace all the occurrences of '\n' with '\r'
170 # so that the potentially multiline sequence can be "serialized" into the env var,
171 # while preserving the information about the line breaks.
172 else
173 MINIMIZED_INPUT="(Input minimization failed, see the workflow logs and artifacts)"
174 fi
175 echo "MINIMIZED_INPUT: '$MINIMIZED_INPUT'"
176 echo "MINIMIZED_INPUT=$MINIMIZED_INPUT" >> "$GITHUB_ENV"
177
178 # Get the workflow agent system info:
179 WF_AGENT_SYS_INFO="`uname -a`"
180 echo "WF_AGENT_SYS_INFO: $WF_AGENT_SYS_INFO"
181 echo "WF_AGENT_SYS_INFO=$WF_AGENT_SYS_INFO" >> "$GITHUB_ENV"
182 echo "WF_AGENT_OS=${{ matrix.os }}" >> "$GITHUB_ENV"
183
184 # Get the branch info:
185 BRANCH_INFO=`git branch | grep '*'`
186 echo "BRANCH_INFO: '$BRANCH_INFO'"
187 echo "BRANCH_INFO=$BRANCH_INFO" >> "$GITHUB_ENV"
188
189 # Get the commit info:
190 COMMIT_INFO=`git log -1 | tr "\n" "\r"`
191 echo "COMMIT_INFO: '$COMMIT_INFO'"
192 echo "COMMIT_INFO=$COMMIT_INFO" >> "$GITHUB_ENV"
193
194 # Get the last N bytes of the fuzzing stderr log into the env var
195 # (N is such that the subsequent GitHub issue reporting does not overflow):
196 STDERR_LOG=`tail -c 63488 $STDERR_LOG_FNAME | tr "\n" "\r"`
197 echo "STDERR_LOG: '$STDERR_LOG'"
198 echo "STDERR_LOG=$STDERR_LOG" >> "$GITHUB_ENV"
199
200 - name: "If Fuzzing Failed: Upload Failure Artifacts"
201 if: failure()
202 uses: actions/upload-artifact@v6
203 with:
204 name: ${{ matrix.target_name }}-fuzz-failure-artifacts
205 path: |
206 ${{ env.OWNER_RDPATH }}/${{ env.STDERR_LOG_FNAME }}
207 ${{ env.OWNER_RDPATH }}/${{ env.TMIN_LOG_FNAME }}
208 ${{ env.OWNER_RDPATH }}/${{ env.ARTIFACTS_RDPATH }}/${{ matrix.target_name }}/*
209 ${{ env.OWNER_RDPATH }}/${{ env.SEEDS_RDPATH }}/${{ matrix.target_name }}/${{ env.SEEDS_FNAME }}
210 if-no-files-found: error
211
212 - name: "If Fuzzing Failed: Report GutHub Issue"
213 if: failure()
214 id: create-issue
215 env:
216 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
217 WORKFLOW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
218 run: |
219 python - <<'PY'
220 import pathlib
221 body_text = """
222 The fuzz-testing workflow has detected a bug.
223
224 <details><summary><b>Auto-Minimized Fuzzing Input That Triggers the Bug:</b> Click this line.</summary>
225 <b>Note:</b> If the input is multi-line then the end-of-line characters '\n' (0x0A) and '\r' (0x0D)
226 may affect the reproducibility of the bug. If you fail to repro the bug with the input shown below
227 then you may want to go to the <a href=${{ env.WORKFLOW_RUN_URL }}>workflow</a> that reported this GitHub bug,
228 download the artifact, and extract the file with the exact minimized input.
229
230 ```qs
231 ${{ env.MINIMIZED_INPUT }}
232 ```
233
234 </details>
235
236 <details><summary><b>The branch/commit the bug has been found in:</b> Click.</summary>
237 If the developers fail to repro the bug in the latest <code>main</code> then the branch/commit info below can help them to make sure
238 that they are using the correct way to repro. If the bug is reproducible in the branch/commit below, but not in latest <code>main</code>,
239 then the bug is likely fixed already or is not in the <code>main</code> branch.
240
241 ```log
242 Branch: ${{ env.BRANCH_INFO }}
243
244 ${{ env.COMMIT_INFO }}
245 ```
246
247 </details>
248
249 **Other Info**
250
251 - [**Workflow**](${{ env.WORKFLOW_RUN_URL }}) (contains the run artifacts).
252 - **Workflow Agent System Info:** `${{ env.WF_AGENT_OS }}: ${{ env.WF_AGENT_SYS_INFO }}`.
253 """
254 pathlib.Path("/tmp/fuzz_issue.md").write_text(body_text)
255 PY
256
257 url="$(gh issue create \
258 --title "Fuzz: \"${{ env.PANIC_MESSAGE }}\" (${{ env.WF_AGENT_OS }})" \
259 --body-file /tmp/fuzz_issue.md \
260 --label bug \
261 --assignee swernli)"
262 number="$(gh issue view "$url" --json number --jq .number)"
263
264 echo "url=$url" >> "$GITHUB_OUTPUT"
265 echo "number=$number" >> "$GITHUB_OUTPUT"
266
267 - name: "If Fuzzing Failed: Log Issue Info"
268 if: failure()
269 run: |
270 echo "Created issue #${{ steps.create-issue.outputs.number }} ${{ steps.create-issue.outputs.url }}"
271