libmicrohttpd

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

make_seed_corpus.sh (4610B)


      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 PATCHES="${SRCDIR}/patches"
     44 
     45 FUZZERS="fuzz_request fuzz_options fuzz_eventloop fuzz_str fuzz_memorypool fuzz_auth_header fuzz_postprocessor"
     46 
     47 if [ ! -d "${CORPUS}" ]; then
     48   echo "ERROR: no corpus directory at ${CORPUS}" >&2
     49   exit 1
     50 fi
     51 
     52 mkdir -p "${OUTDIR}"
     53 
     54 STAGE="$(mktemp -d "${TMPDIR:-/tmp}/mhd-seed-corpus.XXXXXX")"
     55 trap 'rm -rf "${STAGE}"' EXIT
     56 
     57 for fuzzer in ${FUZZERS}; do
     58   dir="${STAGE}/${fuzzer}"
     59   mkdir -p "${dir}"
     60 
     61   n=0
     62   for f in "${CORPUS}/${fuzzer}"-*.bin; do
     63     [ -f "${f}" ] || continue
     64     cp "${f}" "${dir}/$(basename "${f}")"
     65     n=$((n + 1))
     66   done
     67 
     68   # Only the ones whose defect is already fixed are shipped.  While a
     69   # finding is open its proposed fix is kept as an unapplied diff in
     70   # patches/$ID.diff, and its reproducer crashes the target by
     71   # construction; shipping it would make every ClusterFuzz run start by
     72   # rediscovering a bug that is already written down, and bury the
     73   # findings that are actually new.  Committing the fix deletes the diff,
     74   # and that alone promotes the reproducer to a permanent regression seed
     75   # here, with no further edit.
     76   #
     77   # patches/ therefore does not exist while nothing is open, which is the
     78   # normal state; the test below simply never fires then.
     79   #
     80   # A reproducer belongs to the harness named in its file name,
     81   # "K<n>-<harness>-<what>.bin"; the older ones predate that convention
     82   # and are all fuzz_request inputs, so a name without a harness means
     83   # fuzz_request.
     84   if [ -d "${FINDINGS}" ]; then
     85     for f in "${FINDINGS}"/*.bin; do
     86       [ -f "${f}" ] || continue
     87       owner="$(basename "${f}" | sed -n 's/^K[0-9]*-\(fuzz_[a-z_]*\)-.*/\1/p')"
     88       [ -n "${owner}" ] || owner="fuzz_request"
     89       [ "${owner}" = "${fuzzer}" ] || continue
     90       id="$(basename "${f}" | sed -n 's/^\(K[0-9]*\).*/\1/p')"
     91       if [ -n "${id}" ] && [ -f "${PATCHES}/${id}.diff" ]; then
     92         echo "  skipping ${fuzzer} seed $(basename "${f}"): ${id} is open" \
     93              "(${PATCHES}/${id}.diff)"
     94         continue
     95       fi
     96       cp "${f}" "${dir}/known-finding-$(basename "${f}")"
     97       n=$((n + 1))
     98     done
     99   fi
    100 
    101   if [ "${n}" -eq 0 ]; then
    102     echo "ERROR: no seeds found for ${fuzzer} in ${CORPUS}" >&2
    103     exit 1
    104   fi
    105 
    106   zip_path="${OUTDIR}/${fuzzer}_seed_corpus.zip"
    107   rm -f "${zip_path}"
    108   # -j: flat archive, which is what ClusterFuzz expects.
    109   # -X: no extra file attributes, so the zip stays reproducible.
    110   zip -q -j -X "${zip_path}" "${dir}"/* || {
    111     echo "ERROR: failed to create ${zip_path}" >&2
    112     exit 1
    113   }
    114   echo "  ${zip_path}: ${n} seed(s)"
    115 done