nightly_fuzzer.yml 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. name: Nightly Fuzz Testing
  2. run-name: 🌙 Nightly Fuzz Testing ${{ github.sha }}
  3. on:
  4. # 📅 Run this workflow every day at 23:00 UTC
  5. schedule:
  6. - cron: '00 23 * * *'
  7. # 🚀 Also run when this file is updated in any branch
  8. push:
  9. branches:
  10. - '**'
  11. paths:
  12. - '.github/workflows/nightly_fuzzer.yml'
  13. # 🔁 Cancel older runs if a new one starts on the same branch
  14. concurrency:
  15. group: nightly_fuzzing_${{ github.ref }}
  16. cancel-in-progress: true
  17. jobs:
  18. recover_build_from_cache:
  19. name: ♻️ Recover build from cache
  20. runs-on: ubuntu-22.04
  21. outputs:
  22. cache-hit: ${{ steps.check-cache.outputs.cache-hit }}
  23. steps:
  24. - name: 📥 Checkout repo
  25. uses: actions/checkout@v3
  26. - name: 💾 Attempt to restore build cache
  27. id: check-cache
  28. uses: actions/cache@v4
  29. with:
  30. path: build
  31. key: build-linux-fuzz-${{ github.sha }}
  32. # key: build-linux-fuzz
  33. - name: 📦 Upload restored build (if cache was found)
  34. if: steps.check-cache.outputs.cache-hit == 'true'
  35. uses: actions/upload-artifact@v4
  36. with:
  37. name: fuzz-build
  38. path: build
  39. build_linux_fuzz:
  40. name: 🛠️ Build with fuzzer (Linux)
  41. needs: recover_build_from_cache
  42. if: needs.recover_build_from_cache.outputs.cache-hit != 'true'
  43. uses: ./.github/workflows/build_template.yml
  44. with:
  45. CTEST_CONFIGURATION_TYPE: "Debug"
  46. artifact_name: fuzz-build
  47. artifact_list: "build"
  48. cache_key: build-linux-fuzz-cache-dir
  49. cmake_command: |
  50. mkdir build && cd build
  51. export CC=`which clang`
  52. export CXX=`which clang++`
  53. cmake -DQFUZZER=ON ..
  54. cmake --build . --target qfuzzer
  55. cache_build_artifact:
  56. name: 🧊 Save build to cache (if new)
  57. needs: build_linux_fuzz
  58. if: needs.build_linux_fuzz.result == 'success'
  59. runs-on: ubuntu-22.04
  60. steps:
  61. - name: 📥 Download built artifact
  62. uses: actions/download-artifact@v4
  63. with:
  64. name: fuzz-build
  65. path: build
  66. - name: 💾 Save build to cache
  67. uses: actions/cache/save@v4
  68. with:
  69. path: build
  70. key: build-linux-fuzz-${{ github.sha }}
  71. # key: build-linux-fuzz
  72. test_linux_fuzz:
  73. name: 🧪 Run fuzz tests
  74. needs:
  75. - recover_build_from_cache
  76. - build_linux_fuzz
  77. if: always() && (needs.recover_build_from_cache.outputs.cache-hit == 'true' || needs.build_linux_fuzz.result == 'success')
  78. runs-on: ubuntu-22.04
  79. defaults:
  80. run:
  81. shell: bash
  82. container:
  83. image: ubuntu:jammy
  84. steps:
  85. - name: 📥 Checkout repository
  86. uses: actions/checkout@v3
  87. with:
  88. token: ${{ secrets.GITHUB_TOKEN }}
  89. set-safe-directory: true
  90. - name: 📦 Download built artifact with retry logic
  91. uses: manticoresoftware/download_artifact_with_retries@v3
  92. continue-on-error: true
  93. with:
  94. name: fuzz-build
  95. path: .
  96. - name: Restore cache
  97. uses: actions/cache@v4
  98. with:
  99. path: build/src/fuzzer/corpus
  100. key: fuzz-corpus-cache-to-delete
  101. restore-keys: |
  102. fuzz-corpus-
  103. - name: 🐛 Run fuzzer
  104. id: run
  105. continue-on-error: true
  106. run: |
  107. apt -y update && apt -y install llvm
  108. set +e # allow script to continue even if a command fails
  109. cd build/src/fuzzer
  110. mkdir -p corpus
  111. echo "## 🐛 Fuzzer Results" >> $GITHUB_STEP_SUMMARY
  112. # Run qfuzzer and capture its exit code
  113. export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-14
  114. ./qfuzzer -verbosity=0 -max_total_time=12000 -jobs=5 -workers=5-dict=../../../src/fuzzer/dict.txt corpus 2>&1 | tee fuzzer_output.txt
  115. qfuzzer_exit_code=${PIPESTATUS[0]}
  116. echo '```' >> $GITHUB_STEP_SUMMARY
  117. cat fuzzer_output.txt >> $GITHUB_STEP_SUMMARY
  118. echo '```' >> $GITHUB_STEP_SUMMARY
  119. echo "corpus_size=$(ls corpus | wc -l)" >> $GITHUB_OUTPUT
  120. # exit with qfuzzer's code (1 if it failed)
  121. exit $qfuzzer_exit_code
  122. - name: Save cache
  123. if: always()
  124. uses: actions/cache/save@v4
  125. with:
  126. path: build/src/fuzzer/corpus
  127. key: fuzz-corpus-${{ steps.run.outputs.corpus_size || 'unknown' }}
  128. - name: 📦 Upload fuzzer directory
  129. if: always()
  130. uses: actions/upload-artifact@v4
  131. with:
  132. name: fuzzer-dir
  133. path: |
  134. build/src/fuzzer
  135. !build/src/fuzzer/qfuzzer
  136. !build/src/fuzzer/corpus
  137. !build/src/fuzzer/CMake*
  138. !build/src/fuzzer/cmake*
  139. !build/src/fuzzer/CTest*
  140. - name: 📦 Upload updated fuzzer corpus
  141. if: always()
  142. uses: actions/upload-artifact@v4
  143. with:
  144. name: fuzzer-corpus
  145. path: build/src/fuzzer/corpus
  146. - name: ❌ Fail job if fuzzer failed
  147. if: steps.run.outcome == 'failure'
  148. run: |
  149. echo "Fuzzer failed. Marking the job as failed."
  150. exit 1
  151. upload_corpus_to_s3:
  152. name: 📦 Upload fuzzer corpus backup to Manticore S3
  153. needs: test_linux_fuzz
  154. if: always()
  155. runs-on: ubuntu-22.04
  156. steps:
  157. - name: 📥 Download fuzzer-corpus artifact
  158. uses: actions/download-artifact@v4
  159. with:
  160. name: fuzzer-corpus
  161. path: corpus
  162. - name: ☁️ Upload updated fuzzer corpus backup to Manticore S3
  163. run: |
  164. if [ -n "$(find corpus -maxdepth 1 -type f -print -quit)" ]; then
  165. tar -czf corpus.tar.gz corpus
  166. mkdir -p corpus-backup
  167. mv corpus.tar.gz corpus-backup
  168. cd corpus-backup
  169. docker run --rm -v "$(pwd)":/upload manticoresearch/upload fuzz-tests-corpus
  170. else
  171. echo "Directory corpus is empty. Skipping corpus upload."
  172. fi
  173. delete-cache:
  174. name: 🧹 Delete old cache
  175. needs: test_linux_fuzz
  176. if: always()
  177. runs-on: ubuntu-latest
  178. permissions:
  179. actions: write
  180. steps:
  181. - name: Delete cache with key "fuzz-corpus-cache-to-delete"
  182. run: |
  183. curl -s -X DELETE \
  184. -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
  185. -H "Accept: application/vnd.github+json" \
  186. https://api.github.com/repos/${{ github.repository }}/actions/caches?key=fuzz-corpus-cache-to-delete