microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
fe56152d1040a7455141ba110fa462d0a7459e32

Branches

Tags

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

Clone

HTTPS

Download ZIP

.github/workflows/fuzz.yml

208lines · modecode

1name: fuzz
2run-name: Fuzz
3env:
4 OWNER_RDPATH: . # 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 GH_ISSUE_TEMPLATE_RFPATH: .github/ISSUE_TEMPLATE/fuzz_bug_report.md
9 # GitHub issue template rel file path.
10 TARGET_NAME: compile # Fuzzing target name. Fuzzes the `compile` func of the Q# compiler.
11 ARTIFACTS_RDPATH: fuzz/artifacts # Fuzzing artifacts rel dir path.
12 SEEDS_RDPATH: fuzz/seed_inputs # Fuzzing seed inputs rel dir path.
13 SEEDS_FNAME: list.txt # Fuzzing seed inputs list file name.
14on:
15 schedule: # Production runs against default branch.
16 - cron: '24 11 * * 0' # Run after 11:24 UTC (4:24am PDT/3:24am PST) on Sun.
17 workflow_dispatch: # Manual runs.
18 push:
19 branches:
20 - main # Development runs against main branch.
21 paths:
22 - 'compiler/**' # Run if the compiler was changed.
23 - 'fuzz/**' # Run if the fuzzing infra was changed.
24 - '.github/ISSUE_TEMPLATE/fuzz_bug_report.md'
25 # Run if the GitHub issue template was changed.
26 - '.github/workflows/fuzz.yml' # Run if the workflow itself was changed.
27 - '!compiler/qsc_eval/**' # Exclude the qsc_eval dir.
28 - '!compiler/qsc_codegen/**' # Exclude the qsc_codegen dir.
29
30jobs:
31 fuzz:
32 name: Fuzzing
33 strategy:
34 matrix:
35 os: [ubuntu-latest] # Fuzzing is not supported on Win. The macos is temporarily removed
36 # because of low availability.
37 runs-on: ${{ matrix.os }}
38
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@v3
48 with:
49 submodules: "true"
50
51 - name: Gather the Seed Inputs
52 run: |
53 cd $OWNER_RDPATH # Enter the dir containing the fuzzing infra.
54
55 # Clone the submodules of QDK:
56 REPOS="Quantum Quantum-NC QuantumKatas QuantumLibraries iqsharp qdk-python qsharp-compiler qsharp-runtime"
57 for REPO in $REPOS ; do
58 git clone --depth 1 --single-branch --no-tags --recurse-submodules --shallow-submodules --jobs 4 \
59 https://github.com/microsoft/$REPO.git $SEEDS_RDPATH/$TARGET_NAME/$REPO
60 done
61
62 # Build a comma-separated list of all the .qs files in $SEEDS_FNAME file:
63 find $SEEDS_RDPATH/$TARGET_NAME -name "*.qs" | tr "\n" "," > \
64 $SEEDS_RDPATH/$TARGET_NAME/$SEEDS_FNAME
65
66 - name: Build and Run the Fuzz Target
67 run: |
68 cd $OWNER_RDPATH # Enter the dir containing the fuzzing infra.
69 cargo fuzz build --release --sanitizer=none --features do_fuzz $TARGET_NAME # Build the fuzz target.
70
71 # Run fuzzing for specified number of seconds and redirect the `stderr` to a file
72 # whose name is specified by the STDERR_LOG_FNAME env var:
73 RUST_BACKTRACE=1 cargo fuzz run --release --sanitizer=none --features do_fuzz $TARGET_NAME -- \
74 -seed_inputs=@$SEEDS_RDPATH/$TARGET_NAME/$SEEDS_FNAME \
75 -max_total_time=$DURATION_SEC \
76 -rss_limit_mb=4096 \
77 -max_len=20000 \
78 2>$STDERR_LOG_FNAME
79 # The `-rss_limit_mb` and `-max_len` work around running out of memory.
80
81 - name: "If Fuzzing Failed: Collect Failure Info"
82 if: failure()
83 run: |
84 cd $OWNER_RDPATH # Enter the dir containing the fuzzing infra.
85
86 # Extract from stderr log the panic message:
87 PANIC_MESSAGE=`cat $STDERR_LOG_FNAME |
88 grep "panicked at" | sed "s|thread '<unnamed>' panicked at '\([^']*\).*|\1|"`
89 # Explanation:
90 # `cat $STDERR_LOG_FNAME |`: Display the contents of the stderr log file and pass the contents
91 # to the next command.
92 # `grep "panicked at" |`: Filter out (drop) all the lines except the ones containing "panicked at",
93 # the script expects that there is only one such line, pass that line to the next command. Line example:
94 # thread '<unnamed>' panicked at 'global item should have type', . . ./compiler/qsc_frontend/src/typeck/rules.rs:300:26
95 # `sed "s|thread '<unnamed>' panicked at '\([^']*\).*|\1|"`: `sed` - stream editor.
96 # `s` after quote: search command. After `s` there are two sections, each between a pair of '|'.
97 # First section:
98 # In the incoming stream search for a sequence starting with "thread '<unnamed>' panicked at '"
99 # (sequence from the beginning of the line until after the apostrophe where the panic message starts),
100 # followed by zero or more ('*' after ']') non-apostrophe chars (`[^']`)
101 # and memorize ( `\(`, `\)` ) that sequence of non-apostrophe chars (between apostrophes -
102 # "global item should have type") as a memory item 1;
103 # followed by zero or more ('*' after '.') arbitrary chars ('.') till the end of the line.
104 # Second section (`\1`):
105 # If the sequence specified by the first section is found, then replace that sequence (the whole line)
106 # with the memory item 1 (`\1`), ending up in a panic message between the apostrophes.
107 # PANIC_MESSAGE=`. . .`: The output of the command(s) between the backticks ('`') is saved in the
108 # env var PANIC_MESSAGE.
109 # If the failure is not panic-based then extract any ERROR message(s):
110 if [ "$PANIC_MESSAGE" == "" ]; then
111 PANIC_MESSAGE=`cat $STDERR_LOG_FNAME | grep "ERROR"`
112 fi
113 echo "PANIC_MESSAGE: '$PANIC_MESSAGE'" # Output the PANIC_MESSAGE var value to the log
114 # (optional, for workflow failure analysis and sanity check).
115 echo "PANIC_MESSAGE=$PANIC_MESSAGE" >> "$GITHUB_ENV" # Save the PANIC_MESSAGE var in the env, will be used in
116 # the subsequent `run:` and `uses:` steps.
117
118 # Determine the name of a file containing the input of interest (that triggers the panic/crash):
119 if [ -e $ARTIFACTS_RDPATH/$TARGET_NAME/crash-* ]; then # Panic and Stack Overflow Cases.
120 TO_MINIMIZE_FNAME=crash-*;
121 elif [ -e $ARTIFACTS_RDPATH/$TARGET_NAME/oom-* ]; then # Out-of-Memory Case.
122 TO_MINIMIZE_FNAME=oom-*;
123 else
124 echo -e "File to minimize not found.\nContents of artifacts dir \"$ARTIFACTS_RDPATH/$TARGET_NAME/\":"
125 ls $ARTIFACTS_RDPATH/$TARGET_NAME/
126 fi
127
128 if [ "$TO_MINIMIZE_FNAME" != "" ]; then
129 echo "TO_MINIMIZE_FNAME: $TO_MINIMIZE_FNAME"
130
131 # Minimize the input:
132 ( cargo fuzz tmin --release --sanitizer=none --features do_fuzz -r 10000 $TARGET_NAME $ARTIFACTS_RDPATH/$TARGET_NAME/$TO_MINIMIZE_FNAME 2>&1 ) > \
133 $TMIN_LOG_FNAME || MINIMIZATION_FAILED=1
134
135 # Get the minimized input relative faile path:
136 if [ "$MINIMIZATION_FAILED" == "1" ]; then
137 # Minimization failed, get the latest successful minimized input relative faile path:
138 MINIMIZED_INPUT_RFPATH=`
139 cat $TMIN_LOG_FNAME | grep "CRASH_MIN: minimizing crash input: " | tail -n 1 |
140 sed "s|^.*\($ARTIFACTS_RDPATH/$TARGET_NAME/[^\']*\).*|\1|"`
141 else
142 # Minimization Succeeded, get the reported minimized input relative faile path::
143 MINIMIZED_INPUT_RFPATH=`
144 cat $TMIN_LOG_FNAME | grep "failed to minimize beyond" |
145 sed "s|.*\($ARTIFACTS_RDPATH/$TARGET_NAME/[^ ]*\).*|\1|" `
146 fi
147 echo "MINIMIZED_INPUT_RFPATH: $MINIMIZED_INPUT_RFPATH"
148 echo "MINIMIZED_INPUT_RFPATH=$MINIMIZED_INPUT_RFPATH" >> "$GITHUB_ENV"
149
150 # Extract the minimized input:
151 MINIMIZED_INPUT=`cat $MINIMIZED_INPUT_RFPATH | tr "\n" "\r"`
152 # Display the contents of the minimized input file and replace all the occurrences of '\n' with '\r'
153 # so that the potentially multiline sequence can be "serialized" into the env var,
154 # while preserving the information about the line breaks.
155 else
156 MINIMIZED_INPUT="(Input minimization failed, see the workflow logs and artifacts)"
157 fi
158 echo "MINIMIZED_INPUT: '$MINIMIZED_INPUT'"
159 echo "MINIMIZED_INPUT=$MINIMIZED_INPUT" >> "$GITHUB_ENV"
160
161 # Get the workflow agent system info:
162 WF_AGENT_SYS_INFO="`uname -a`"
163 echo "WF_AGENT_SYS_INFO: $WF_AGENT_SYS_INFO"
164 echo "WF_AGENT_SYS_INFO=$WF_AGENT_SYS_INFO" >> "$GITHUB_ENV"
165 echo "WF_AGENT_OS=${{ matrix.os }}" >> "$GITHUB_ENV"
166
167 # Get the branch info:
168 BRANCH_INFO=`git branch | grep '*'`
169 echo "BRANCH_INFO: '$BRANCH_INFO'"
170 echo "BRANCH_INFO=$BRANCH_INFO" >> "$GITHUB_ENV"
171
172 # Get the commit info:
173 COMMIT_INFO=`git log -1 | tr "\n" "\r"`
174 echo "COMMIT_INFO: '$COMMIT_INFO'"
175 echo "COMMIT_INFO=$COMMIT_INFO" >> "$GITHUB_ENV"
176
177 # Get the last N bytes of the fuzzing stderr log into the env var
178 # (N is such that the subsequent GitHub issue reporting does not overflow):
179 STDERR_LOG=`tail -c 63488 $STDERR_LOG_FNAME | tr "\n" "\r"`
180 echo "STDERR_LOG: '$STDERR_LOG'"
181 echo "STDERR_LOG=$STDERR_LOG" >> "$GITHUB_ENV"
182
183 - name: "If Fuzzing Failed: Upload Failure Artifacts"
184 if: failure()
185 uses: actions/upload-artifact@v3
186 with:
187 path: |
188 ${{ env.OWNER_RDPATH }}/${{ env.STDERR_LOG_FNAME }}
189 ${{ env.OWNER_RDPATH }}/${{ env.TMIN_LOG_FNAME }}
190 ${{ env.OWNER_RDPATH }}/${{ env.ARTIFACTS_RDPATH }}/${{ env.TARGET_NAME }}/*
191 ${{ env.OWNER_RDPATH }}/${{ env.SEEDS_RDPATH }}/${{ env.TARGET_NAME }}/${{ env.SEEDS_FNAME }}
192 if-no-files-found: error
193
194 - name: "If Fuzzing Failed: Report GutHub Issue"
195 if: failure()
196 uses: JasonEtco/create-an-issue@v2
197 env:
198 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
199 WORKFLOW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
200 with:
201 filename: ${{ env.GH_ISSUE_TEMPLATE_RFPATH }}
202 # This issue template file uses a number of env vars collected above.
203 id: create-issue
204
205 - name: "If Fuzzing Failed: Log Issue Info"
206 if: failure()
207 run: |
208 echo "Created issue #${{ steps.create-issue.outputs.number }} ${{ steps.create-issue.outputs.url }}"