libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

make_seed_corpus.sh (5177B)


      1 #!/bin/bash -eu
      2 #
      3 # Package src/fuzz/corpus/ into the $OUT/<fuzzer>_seed_corpus.zip files that
      4 # OSS-Fuzz/ClusterFuzz picks up automatically.
      5 #
      6 # This file is in the public domain.
      7 #
      8 # Usage:  make_seed_corpus.sh [SRCDIR] [OUTDIR]
      9 #
     10 #   SRCDIR   top of the libmicrohttpd source tree   (default: derived from $0)
     11 #   OUTDIR   where the zips are written             (default: $OUT, else ./out)
     12 #
     13 # Corpus layout in src/fuzz/corpus/:
     14 #
     15 #   fuzz_<harness>-NN.bin     per-harness seeds; the file name prefix is the
     16 #                             harness the seed belongs to.  Inputs are NOT
     17 #                             interchangeable between harnesses: byte 0 of
     18 #                             every harness input selects a different thing.
     19 #   known-findings/K*.bin     byte-exact reproducers for the findings in
     20 #                             src/fuzz/README section 6.  Each goes into the
     21 #                             seed corpus of the harness that found it, where
     22 #                             OSS-Fuzz keeps re-running it forever - i.e. it
     23 #                             becomes a permanent regression test.  The owning
     24 #                             harness is named in the file, "K<n>-<harness>-
     25 #                             <what>.bin"; a name without one means
     26 #                             fuzz_request, which is what the reproducers
     27 #                             predating the convention are.  Reproducers of
     28 #                             findings that are still open are skipped; see
     29 #                             the loop below.
     30 #   README                    documentation, not an input; excluded.
     31 #
     32 # The corpus itself is regenerated from the harnesses' built-in seeds with
     33 #   make -C src/fuzz refresh-corpus
     34 # (which runs "./fuzz_<name> --write-corpus=corpus" for every harness).
     35 # known-findings/ is hand-maintained and is never touched by that.
     36 
     37 SELF_DIR="$(cd "$(dirname "$0")" && pwd)"
     38 SRCDIR="${1:-$(cd "${SELF_DIR}/../.." && pwd)}"
     39 OUTDIR="${2:-${OUT:-$(pwd)/out}}"
     40 
     41 CORPUS="${SRCDIR}/src/fuzz/corpus"
     42 FINDINGS="${CORPUS}/known-findings"
     43 DISTILLED="${CORPUS}/distilled"
     44 PATCHES="${SRCDIR}/patches"
     45 
     46 FUZZERS="fuzz_request fuzz_options fuzz_eventloop fuzz_str fuzz_memorypool fuzz_auth_header fuzz_postprocessor"
     47 
     48 if [ ! -d "${CORPUS}" ]; then
     49   echo "ERROR: no corpus directory at ${CORPUS}" >&2
     50   exit 1
     51 fi
     52 
     53 mkdir -p "${OUTDIR}"
     54 
     55 STAGE="$(mktemp -d "${TMPDIR:-/tmp}/mhd-seed-corpus.XXXXXX")"
     56 trap 'rm -rf "${STAGE}"' EXIT
     57 
     58 for fuzzer in ${FUZZERS}; do
     59   dir="${STAGE}/${fuzzer}"
     60   mkdir -p "${dir}"
     61 
     62   n=0
     63   for f in "${CORPUS}/${fuzzer}"-*.bin; do
     64     [ -f "${f}" ] || continue
     65     cp "${f}" "${dir}/$(basename "${f}")"
     66     n=$((n + 1))
     67   done
     68 
     69   # Only the ones whose defect is already fixed are shipped.  While a
     70   # finding is open its proposed fix is kept as an unapplied diff in
     71   # patches/$ID.diff, and its reproducer crashes the target by
     72   # construction; shipping it would make every ClusterFuzz run start by
     73   # rediscovering a bug that is already written down, and bury the
     74   # findings that are actually new.  Committing the fix deletes the diff,
     75   # and that alone promotes the reproducer to a permanent regression seed
     76   # here, with no further edit.
     77   #
     78   # patches/ therefore does not exist while nothing is open, which is the
     79   # normal state; the test below simply never fires then.
     80   #
     81   # A reproducer belongs to the harness named in its file name,
     82   # "K<n>-<harness>-<what>.bin"; the older ones predate that convention
     83   # and are all fuzz_request inputs, so a name without a harness means
     84   # fuzz_request.
     85   if [ -d "${FINDINGS}" ]; then
     86     for f in "${FINDINGS}"/*.bin; do
     87       [ -f "${f}" ] || continue
     88       owner="$(basename "${f}" | sed -n 's/^K[0-9]*-\(fuzz_[a-z_]*\)-.*/\1/p')"
     89       [ -n "${owner}" ] || owner="fuzz_request"
     90       [ "${owner}" = "${fuzzer}" ] || continue
     91       id="$(basename "${f}" | sed -n 's/^\(K[0-9]*\).*/\1/p')"
     92       if [ -n "${id}" ] && [ -f "${PATCHES}/${id}.diff" ]; then
     93         echo "  skipping ${fuzzer} seed $(basename "${f}"): ${id} is open" \
     94              "(${PATCHES}/${id}.diff)"
     95         continue
     96       fi
     97       cp "${f}" "${dir}/known-finding-$(basename "${f}")"
     98       n=$((n + 1))
     99     done
    100   fi
    101 
    102   # distilled/ is the edge-minimal residue of a fuzzing campaign, named
    103   # "<harness>-dNNN.bin" and therefore routed by prefix like the seeds
    104   # above.  It is by far the largest part of the seed corpus and the
    105   # reason a fresh ClusterFuzz run starts near the coverage the last
    106   # campaign reached instead of climbing back to it.
    107   if [ -d "${DISTILLED}" ]; then
    108     for f in "${DISTILLED}/${fuzzer}"-*.bin; do
    109       [ -f "${f}" ] || continue
    110       cp "${f}" "${dir}/distilled-$(basename "${f}")"
    111       n=$((n + 1))
    112     done
    113   fi
    114 
    115   if [ "${n}" -eq 0 ]; then
    116     echo "ERROR: no seeds found for ${fuzzer} in ${CORPUS}" >&2
    117     exit 1
    118   fi
    119 
    120   zip_path="${OUTDIR}/${fuzzer}_seed_corpus.zip"
    121   rm -f "${zip_path}"
    122   # -j: flat archive, which is what ClusterFuzz expects.
    123   # -X: no extra file attributes, so the zip stays reproducible.
    124   zip -q -j -X "${zip_path}" "${dir}"/* || {
    125     echo "ERROR: failed to create ${zip_path}" >&2
    126     exit 1
    127   }
    128   echo "  ${zip_path}: ${n} seed(s)"
    129 done