build.sh (20039B)
1 #!/bin/bash -eu 2 # 3 # OSS-Fuzz build script for GNU libextractor. 4 # 5 # This file is in the public domain. 6 # 7 # It is executed inside the OSS-Fuzz base-builder image, which exports: 8 # 9 # $SRC parent directory of the checked-out sources 10 # ($SRC/libextractor, see Dockerfile) 11 # $WORK scratch directory for build artifacts 12 # $OUT where the finished fuzz targets must be installed 13 # $CC $CXX the instrumented compilers 14 # $CFLAGS $CXXFLAGS sanitizer + coverage flags; MUST be honoured and 15 # MUST NOT be replaced 16 # $LIB_FUZZING_ENGINE the fuzzing engine to link against 17 # $SANITIZER address | undefined | memory | coverage 18 # $FUZZING_ENGINE libfuzzer | afl | honggfuzz | centipede | none 19 # 20 # The same script can be run outside OSS-Fuzz for a local campaign; every 21 # variable above has a defensive default below. See contrib/oss-fuzz/README. 22 23 # The shebang carries -eu, but "bash build.sh" silently drops it, and a 24 # harness that fails to link would then leave $OUT short one target while 25 # the script still exits 0. 26 set -eu 27 28 SRC="${SRC:-$(cd "$(dirname "$0")/../../.." && pwd)}" 29 WORK="${WORK:-${SRC}/work}" 30 OUT="${OUT:-${SRC}/out}" 31 SANITIZER="${SANITIZER:-address}" 32 FUZZING_ENGINE="${FUZZING_ENGINE:-libfuzzer}" 33 ARCHITECTURE="${ARCHITECTURE:-x86_64}" 34 35 # --- engine ---------------------------------------------------------------- 36 # 37 # Under OSS-Fuzz $CC, $CXX and $LIB_FUZZING_ENGINE come from the 38 # base-builder image and MUST be used as given, so all of this is dead 39 # code there. It fires only on a local run, where its job is to make 40 # "FUZZING_ENGINE=afl ./build.sh" produce a real AFL++ target rather than 41 # a libFuzzer one that happens to link. 42 # 43 # _le_cov_cflags is the coverage instrumentation the engine needs at 44 # compile time. Getting it wrong is the failure mode that matters: the 45 # build succeeds, the target runs, and it finds nothing, because the 46 # engine has no feedback signal at all. 47 case "${FUZZING_ENGINE}" in 48 libfuzzer) 49 CC="${CC:-clang}" 50 CXX="${CXX:-clang++}" 51 LIB_FUZZING_ENGINE="${LIB_FUZZING_ENGINE:--fsanitize=fuzzer}" 52 _le_cov_cflags="-fsanitize=fuzzer-no-link" 53 _le_main="engine" 54 ;; 55 afl) 56 CC="${CC:-afl-clang-fast}" 57 CXX="${CXX:-afl-clang-fast++}" 58 LIB_FUZZING_ENGINE="${LIB_FUZZING_ENGINE:-/usr/lib/afl/libAFLDriver.a}" 59 _le_cov_cflags="" 60 _le_main="engine" 61 ;; 62 honggfuzz) 63 CC="${CC:-hfuzz-clang}" 64 CXX="${CXX:-hfuzz-clang++}" 65 LIB_FUZZING_ENGINE="${LIB_FUZZING_ENGINE:-}" 66 _le_cov_cflags="" 67 _le_main="engine" 68 ;; 69 none) 70 # No engine: link the harnesses' own deterministic driver instead. 71 # This is what makes a sanitizer-only smoke test possible with no 72 # fuzzing engine installed at all. 73 CC="${CC:-clang}" 74 CXX="${CXX:-clang++}" 75 LIB_FUZZING_ENGINE="${LIB_FUZZING_ENGINE:-}" 76 _le_cov_cflags="" 77 _le_main="builtin" 78 ;; 79 *) 80 echo "ERROR: unknown FUZZING_ENGINE='${FUZZING_ENGINE}'" >&2 81 echo " expected: libfuzzer | afl | honggfuzz | none" >&2 82 exit 1 83 ;; 84 esac 85 86 case "${ARCHITECTURE}" in 87 x86_64) _le_arch_cflags="" ;; 88 i386) _le_arch_cflags="-m32 -no-pie" ;; 89 *) 90 echo "ERROR: unknown ARCHITECTURE='${ARCHITECTURE}'" >&2 91 exit 1 92 ;; 93 esac 94 95 # --- $CFLAGS / $CXXFLAGS --------------------------------------------------- 96 # 97 # Dead code under OSS-Fuzz, which always exports these. On a local run 98 # the defaults have to make it a *real* fuzzing build; two flags decide 99 # that, and leaving either out produces a build that looks fine and finds 100 # nothing: 101 # 102 # -fsanitize=fuzzer-no-link installs libFuzzer's coverage 103 # instrumentation in every translation unit. Without it there is 104 # no feedback signal, `cov:` stays flat and the corpus never grows. 105 # a sanitizer libFuzzer only notices a crash the kernel 106 # delivers. ASan is what turns a silently tolerated over-read of 107 # a parser buffer -- the whole bug class this project has -- into 108 # a report. 109 if [ -z "${CFLAGS:-}" ]; then 110 _le_base_cflags="-O1 -fno-omit-frame-pointer -gline-tables-only" 111 112 case "${SANITIZER}" in 113 address) 114 # OSS-Fuzz runs "address" and "undefined" as separate campaigns so 115 # that each report is attributed to one sanitizer. A local run has 116 # an afternoon at most, so the default folds UBSan into the ASan 117 # build: two oracles per CPU-hour instead of one. 118 # 119 # UBSan is deliberately left *recovering* here (no 120 # -fno-sanitize-recover): libextractor's parsers trip a handful of 121 # signed-shift and misaligned-load sites on almost every input, and 122 # halting on those would stop the run before ASan ever gets to the 123 # memory-safety bugs. UBSan reports each source location once per 124 # process, so they still all show up in the log. Set 125 # LE_UBSAN_HALT=1 for the OSS-Fuzz behaviour of dying on the first. 126 _le_san_cflags="-fsanitize=address,undefined" 127 _le_san_cflags="${_le_san_cflags} -fsanitize-address-use-after-scope" 128 if [ "${LE_UBSAN_HALT:-0}" != "0" ]; then 129 _le_san_cflags="${_le_san_cflags} -fno-sanitize-recover=undefined" 130 fi 131 ;; 132 undefined) 133 _le_san_cflags="-fsanitize=undefined -fno-sanitize-recover=undefined" 134 ;; 135 memory) 136 # Only usable when the C library and every dependency is 137 # instrumented too -- true inside the OSS-Fuzz image, essentially 138 # never on a distro toolchain. This is also why the build below 139 # disables every optional third-party parser: an uninstrumented 140 # libjpeg or GnuTLS would poison every run. 141 _le_san_cflags="-fsanitize=memory -fsanitize-memory-track-origins" 142 ;; 143 coverage) 144 _le_san_cflags="-fprofile-instr-generate -fcoverage-mapping" 145 ;; 146 none | "") 147 _le_san_cflags="" 148 ;; 149 *) 150 echo "ERROR: unknown SANITIZER='${SANITIZER}'" >&2 151 exit 1 152 ;; 153 esac 154 155 CFLAGS="${_le_arch_cflags} ${_le_base_cflags} ${_le_san_cflags}" 156 if [ "${SANITIZER}" != "coverage" ]; then 157 CFLAGS="${CFLAGS} ${_le_cov_cflags}" 158 fi 159 unset _le_base_cflags _le_san_cflags 160 fi 161 CXXFLAGS="${CXXFLAGS:-${CFLAGS}}" 162 if [ "${_le_main}" = "builtin" ]; then 163 _le_no_main="" 164 else 165 _le_no_main="-DFUZZ_NO_MAIN" 166 fi 167 unset _le_arch_cflags _le_cov_cflags _le_main 168 169 LE_SRC="${LE_SRC:-${SRC}/libextractor}" 170 171 # Out-of-tree build: build.sh never modifies the checkout, which is what 172 # makes "run it twice" and "reproduce against a pristine tree" work. 173 BUILD="${WORK}/le-build" 174 175 mkdir -p "${WORK}" "${OUT}" "${BUILD}" 176 177 echo "=== libextractor OSS-Fuzz build ===" 178 echo " LE_SRC = ${LE_SRC}" 179 echo " BUILD = ${BUILD}" 180 echo " OUT = ${OUT}" 181 echo " SANITIZER = ${SANITIZER}" 182 echo " FUZZING_ENGINE = ${FUZZING_ENGINE}" 183 echo " LIB_FUZZING_ENGINE = ${LIB_FUZZING_ENGINE}" 184 echo " CC / CXX = ${CC} / ${CXX}" 185 echo " CFLAGS = ${CFLAGS}" 186 187 # --------------------------------------------------------------------------- 188 # 1. Bootstrap (the git checkout ships no 'configure') 189 # --------------------------------------------------------------------------- 190 cd "${LE_SRC}" 191 if [ ! -x ./configure ]; then 192 echo "--- bootstrapping ---" 193 ./bootstrap || true 194 if [ ! -x ./configure ]; then 195 autoreconf -f -I m4 -i 196 fi 197 fi 198 199 # --------------------------------------------------------------------------- 200 # 2. Configure 201 # --------------------------------------------------------------------------- 202 # Rationale for each flag: 203 # 204 # --enable-static 205 # fuzz_datasource, fuzz_unzip and fuzz_ipc call symbols that are 206 # compiled with $(HIDDEN_VISIBILITY_CFLAGS) and are therefore NOT 207 # exported from libextractor.so. Only the static archive can be 208 # linked, and OSS-Fuzz wants static targets anyway: a binary in 209 # $OUT must not depend on anything outside $OUT. 210 # --enable-fuzzing 211 # configures src/fuzz/Makefile. The harnesses are compiled by 212 # hand below, but keeping this on makes configure fail loudly if 213 # src/fuzz/ ever stops being wired up. 214 # --disable-shared 215 # the per-plugin targets link the plugin's own .c files, so no 216 # plugin module is loaded at run time and building them would only 217 # cost time. Note that this is what makes fuzz_extract useless in 218 # this configuration; see the $FUZZERS selection below. 219 # --disable-gsf --disable-glib 220 # step 3 only builds src/common and src/main, so no plugin that 221 # wraps a third-party parser is ever compiled here: those are 222 # fuzzed by their own OSS-Fuzz projects, and an uninstrumented copy 223 # of one would poison the MemorySanitizer build. Turning off the 224 # two that configure would otherwise pull into the *library* link 225 # keeps the dependency set at libc + zlib. 226 # LE_FUZZ_GSF=1 keeps libgsf in, which is what it takes to build the ole2 227 # target (see $GSF_PLUGIN_FUZZER below). Off by default because libgsf 228 # and glib are not instrumented here, so every allocation they make is 229 # invisible to ASan and impossible under MemorySanitizer -- the same 230 # reason the other third-party wrappers are opt-in. Turn it on for a 231 # local campaign: ole2_extractor.c does a good deal of its own parsing 232 # before libgsf is ever called, and that part is ours. 233 _le_gsf_conf="--disable-gsf --disable-glib" 234 if [ "${LE_FUZZ_GSF:-0}" != "0" ]; then 235 _le_gsf_conf="" 236 fi 237 238 cd "${BUILD}" 239 # shellcheck disable=SC2086 240 "${LE_SRC}/configure" \ 241 --enable-static \ 242 --disable-shared \ 243 --with-pic \ 244 --enable-fuzzing \ 245 ${_le_gsf_conf} \ 246 --disable-dependency-tracking \ 247 CC="${CC}" \ 248 CFLAGS="${CFLAGS}" \ 249 LDFLAGS="${LDFLAGS:-}" 250 251 # --------------------------------------------------------------------------- 252 # 3. Build the libraries the harnesses link against 253 # --------------------------------------------------------------------------- 254 make -j"$(nproc)" -C src/common 255 make -j"$(nproc)" -C src/main libextractor.la 256 257 LE_LIB="${BUILD}/src/main/.libs/libextractor.a" 258 LE_COMMON="${BUILD}/src/common/.libs/libextractor_common.a" 259 for f in "${LE_LIB}" "${LE_COMMON}"; do 260 test -f "${f}" || { echo "ERROR: ${f} was not produced" >&2; exit 1; } 261 done 262 263 # The static archives do not record their own dependencies, so read them 264 # off the libtool archives instead of hardcoding a list here. Which ones 265 # are needed depends on what configure found (-lbz2, -lltdl, -ltidy, 266 # -lapparmor, ...), so a hardcoded list would silently rot. 267 le_deps () 268 { 269 sed -n "s/^dependency_libs='\(.*\)'\$/\1/p" "$1" 2>/dev/null 270 } 271 LE_SYSLIBS="$(le_deps "${BUILD}/src/main/libextractor.la") \ 272 $(le_deps "${BUILD}/src/common/libextractor_common.la")" 273 echo " LE_SYSLIBS = ${LE_SYSLIBS}" 274 275 # --------------------------------------------------------------------------- 276 # 4. Compile the harnesses as fuzzing-engine translation units 277 # --------------------------------------------------------------------------- 278 LE_INCLUDES=( 279 -I"${BUILD}" 280 -I"${LE_SRC}" 281 -I"${LE_SRC}/src/include" 282 -I"${LE_SRC}/src/common" 283 -I"${LE_SRC}/src/main" 284 -I"${LE_SRC}/src/plugins" 285 -I"${LE_SRC}/src/fuzz" 286 ) 287 288 # fuzz_extract is deliberately absent: it dlopen()s the installed plugin 289 # modules, which this build does not produce (--disable-shared), and a 290 # target that loads uninstrumented shared objects gives the engine no 291 # feedback from the code it is meant to be fuzzing. It is built and run 292 # in tree by "make -C src/fuzz check" instead. 293 CORE_FUZZERS="fuzz_datasource fuzz_ipc fuzz_convert fuzz_unzip" 294 295 # name:extra sources:extra libs (the plugin's own objects and its 296 # dependencies; keep in sync with src/fuzz/Makefile.am) 297 PLUGIN_FUZZERS=" 298 applefile:applefile_extractor.c pack.c: 299 dvi:dvi_extractor.c fulltext.c pagelayout.c:-lm 300 elf:elf_extractor.c pack.c: 301 it:it_extractor.c: 302 man:man_extractor.c: 303 nsf:nsf_extractor.c: 304 nsfe:nsfe_extractor.c: 305 ps:ps_extractor.c: 306 real:real_extractor.c: 307 riff:riff_extractor.c:-lm 308 rtf:rtf_extractor.c: 309 s3m:s3m_extractor.c: 310 sid:sid_extractor.c: 311 wav:wav_extractor.c: 312 xm:xm_extractor.c: 313 deb:deb_extractor.c:-lz 314 msoffice:msoffice_extractor.c:-lz 315 odf:odf_extractor.c fulltext.c:-lz 316 png:png_extractor.c:-lz 317 qt:qt_extractor.c:-lz 318 zip:zip_extractor.c:-lz 319 pecoff:pecoff_extractor.c forensics.c:-lm 320 lnk:lnk_extractor.c forensics.c:-lm 321 sqlite:sqlite_extractor.c forensics.c:-lm 322 tar:tar_extractor.c forensics.c:-lm 323 iso9660:iso9660_extractor.c forensics.c:-lm 324 diskimage:diskimage_extractor.c forensics.c:-lm 325 heif:heif_extractor.c forensics.c:-lm 326 webp:webp_extractor.c forensics.c:-lm 327 plist:plist_extractor.c forensics.c:-lm 328 id3:id3_extractor.c forensics.c:-lm 329 gpx:gpx_extractor.c forensics.c:-lm 330 kml:kml_extractor.c forensics.c:-lm 331 geotiff:geotiff_extractor.c forensics.c:-lm 332 mbox:mbox_extractor.c forensics.c:-lm 333 apk:apk_extractor.c forensics.c:-lm -lz 334 ebook:ebook_extractor.c forensics.c fulltext.c:-lm -lz 335 " 336 337 # Plugins that wrap a third-party parser. NOT part of the OSS-Fuzz build: 338 # giflib, libjpeg, libtiff, FLAC, libvorbis, libarchive and libmagic all 339 # have their own OSS-Fuzz projects, an uninstrumented copy of one would 340 # attribute its bugs to libextractor, and it would make the 341 # MemorySanitizer configuration impossible. Set LE_FUZZ_EXTRA_PLUGINS=1 342 # for a local campaign that wants the *glue* -- the part that turns what 343 # the library returns into a metadata callback, which is ours -- covered 344 # as well. 345 EXTRA_PLUGIN_FUZZERS=" 346 gif:gif_extractor.c:-lgif 347 jpeg:jpeg_extractor.c:-ljpeg 348 tiff:tiff_extractor.c:-ltiff 349 flac:flac_extractor.c:-lFLAC 350 ogg:ogg_extractor.c:-lvorbisfile -lvorbis -logg 351 archive:archive_extractor.c:-larchive 352 mime:mime_extractor.c:-lmagic 353 html:html_extractor.c fulltext.c:-ltidy -lmagic 354 " 355 356 # ole2 is separate from the list above because it is the one plugin that 357 # needs extra *compiler* flags rather than just a -l, so it can only be 358 # built when configure kept libgsf (LE_FUZZ_GSF=1). It is worth the 359 # trouble: ole2_extractor.c parses the header fields itself before it 360 # hands the stream to libgsf, and a signed-overflow defect sat in exactly 361 # that code until a campaign finally covered this target. 362 GSF_PLUGIN_FUZZER="" 363 if [ "${LE_FUZZ_GSF:-0}" != "0" ]; then 364 if _le_gsf_cflags="$(pkg-config --cflags libgsf-1 2>/dev/null)" && 365 _le_gsf_libs="$(pkg-config --libs libgsf-1 2>/dev/null)"; then 366 GSF_PLUGIN_FUZZER=" 367 ole2:ole2_extractor.c fulltext.c:${_le_gsf_libs} 368 " 369 LE_GSF_CFLAGS="${_le_gsf_cflags}" 370 else 371 echo "WARNING: LE_FUZZ_GSF=1 but pkg-config cannot find libgsf-1;" >&2 372 echo " the ole2 target will be skipped." >&2 373 fi 374 fi 375 376 # pdf is the third such case, and it is excluded here for the same 377 # reason as ole2: libpoppler is not instrumented in this build and has 378 # its own OSS-Fuzz project, so its bugs must not be attributed to us. 379 # But the "fulltext" option made pdf_extractor.cc a good deal more than 380 # a thin wrapper -- the line grouping, the two-column detection, the 381 # margin filtering and all of pagelayout.c run on poppler's *output* and 382 # are ours -- so a local campaign wants it. Set LE_FUZZ_POPPLER=1. 383 # Note that the plugin is C++; build_one dispatches on the extension. 384 POPPLER_PLUGIN_FUZZER="" 385 if [ "${LE_FUZZ_POPPLER:-0}" != "0" ]; then 386 if _le_pop_cflags="$(pkg-config --cflags poppler-cpp 2>/dev/null)" && 387 _le_pop_libs="$(pkg-config --libs poppler-cpp 2>/dev/null)"; then 388 POPPLER_PLUGIN_FUZZER=" 389 pdf:pdf_extractor.cc fulltext.c pagelayout.c:${_le_pop_libs} -lm 390 " 391 LE_POPPLER_CFLAGS="${_le_pop_cflags}" 392 else 393 echo "WARNING: LE_FUZZ_POPPLER=1 but pkg-config cannot find" >&2 394 echo " poppler-cpp; the pdf target will be skipped." >&2 395 fi 396 fi 397 398 if [ "${LE_FUZZ_EXTRA_PLUGINS:-0}" != "0" ]; then 399 PLUGIN_FUZZERS="${PLUGIN_FUZZERS}${EXTRA_PLUGIN_FUZZERS}" 400 fi 401 PLUGIN_FUZZERS="${PLUGIN_FUZZERS}${GSF_PLUGIN_FUZZER}" 402 PLUGIN_FUZZERS="${PLUGIN_FUZZERS}${POPPLER_PLUGIN_FUZZER}" 403 404 # Upper-case the plugin name for -DLE_FUZZ_ID. 405 upper () { echo "$1" | tr '[:lower:]' '[:upper:]'; } 406 407 build_one () 408 { 409 local target="$1"; shift 410 local objs="" 411 local extra_libs="$1"; shift 412 local o 413 414 for src in "$@"; do 415 o="${WORK}/${target}-$(basename "${src}" | sed 's/\.[^.]*$//').o" 416 # pdf_extractor.cc is C++. Dispatch on the extension rather than 417 # letting the driver guess: clang would compile a .cc as C++ anyway, 418 # but it would do so with $CFLAGS, and OSS-Fuzz passes the two 419 # separately precisely because they are not interchangeable. 420 case "${src}" in 421 *.cc | *.cpp | *.cxx) 422 # shellcheck disable=SC2086 423 $CXX $CXXFLAGS ${_le_no_main} ${LE_EXTRA_CPPFLAGS:-} \ 424 "${LE_INCLUDES[@]}" -c "${src}" -o "${o}" 425 ;; 426 *) 427 # shellcheck disable=SC2086 428 $CC $CFLAGS ${_le_no_main} ${LE_EXTRA_CPPFLAGS:-} \ 429 "${LE_INCLUDES[@]}" -c "${src}" -o "${o}" 430 ;; 431 esac 432 objs="${objs} ${o}" 433 done 434 # Link with $CXX: $LIB_FUZZING_ENGINE is a C++ archive for most engines. 435 # shellcheck disable=SC2086 436 $CXX $CXXFLAGS ${objs} -o "${OUT}/${target}" \ 437 $LIB_FUZZING_ENGINE "${LE_LIB}" "${LE_COMMON}" ${extra_libs} \ 438 ${LE_SYSLIBS} -lz -lpthread -ldl 439 } 440 441 FUZZERS="" 442 443 for f in ${CORE_FUZZERS}; do 444 echo "--- building ${f} ---" 445 LE_EXTRA_CPPFLAGS="" \ 446 build_one "${f}" "" "${LE_SRC}/src/fuzz/${f}.c" 447 FUZZERS="${FUZZERS} ${f}" 448 done 449 450 # Iterate line by line, not word by word: the middle field holds a 451 # space-separated source list. A pipe into `while read` would put the 452 # loop in a subshell and lose $FUZZERS, hence the IFS dance. 453 _le_oldifs="${IFS}" 454 IFS=' 455 ' 456 for line in ${PLUGIN_FUZZERS}; do 457 IFS="${_le_oldifs}" 458 [ -n "${line}" ] || { IFS=' 459 '; continue; } 460 name="${line%%:*}" 461 rest="${line#*:}" 462 srcs="${rest%%:*}" 463 libs="${rest#*:}" 464 target="fuzz_${name}" 465 echo "--- building ${target} ---" 466 set -- 467 for s in ${srcs}; do 468 set -- "$@" "${LE_SRC}/src/plugins/${s}" 469 done 470 # ole2_extractor.c includes <gsf/gsf-*.h>, so it is the one plugin that 471 # needs include paths of its own; everything else compiles with the 472 # library's flags alone. 473 _le_plug_cppflags="-DLE_FUZZ_PLUGIN=${name} -DLE_FUZZ_ID=$(upper "${name}")" 474 if [ "${name}" = "ole2" ]; then 475 _le_plug_cppflags="${_le_plug_cppflags} ${LE_GSF_CFLAGS:-}" 476 fi 477 # pdf_extractor.cc includes <poppler/cpp/*.h>; same story as ole2. 478 if [ "${name}" = "pdf" ]; then 479 _le_plug_cppflags="${_le_plug_cppflags} ${LE_POPPLER_CFLAGS:-}" 480 fi 481 LE_EXTRA_CPPFLAGS="${_le_plug_cppflags}" \ 482 build_one "${target}" "${libs}" "${LE_SRC}/src/fuzz/fuzz_plugin.c" "$@" 483 FUZZERS="${FUZZERS} ${target}" 484 IFS=' 485 ' 486 done 487 IFS="${_le_oldifs}" 488 unset _le_oldifs 489 490 # --------------------------------------------------------------------------- 491 # 5. Seed corpora, dictionaries and .options files 492 # --------------------------------------------------------------------------- 493 FUZZBIN="${OUT}" "${LE_SRC}/contrib/oss-fuzz/make_seed_corpus.sh" \ 494 "${LE_SRC}" "${OUT}" 495 496 for f in ${FUZZERS}; do 497 d="${LE_SRC}/contrib/oss-fuzz/dicts/${f}.dict" 498 [ -f "${d}" ] || d="${LE_SRC}/contrib/oss-fuzz/dicts/fuzz_plugin.dict" 499 cp "${d}" "${OUT}/${f}.dict" 500 o="${LE_SRC}/contrib/oss-fuzz/${f}.options" 501 [ -f "${o}" ] || o="${LE_SRC}/contrib/oss-fuzz/default.options" 502 cp "${o}" "${OUT}/${f}.options" 503 done 504 505 # --------------------------------------------------------------------------- 506 # 6. Verify: every requested target must actually be in $OUT 507 # --------------------------------------------------------------------------- 508 # Belt and braces for the failure that matters most -- a build that 509 # reports success but ships nothing, which on OSS-Fuzz shows up only as a 510 # target that never runs. 511 _le_missing="" 512 for f in ${FUZZERS}; do 513 [ -x "${OUT}/${f}" ] || _le_missing="${_le_missing} ${f}" 514 [ -f "${OUT}/${f}_seed_corpus.zip" ] || 515 _le_missing="${_le_missing} ${f}_seed_corpus.zip" 516 done 517 if [ -n "${_le_missing}" ]; then 518 echo "ERROR: build did not produce:${_le_missing}" >&2 519 exit 1 520 fi 521 unset _le_missing 522 523 echo "=== done; contents of \$OUT ===" 524 ls -la "${OUT}"