libmicrohttpd

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

2-matrix.sh (9539B)


      1 #!/bin/bash
      2 #
      3 # The build-configuration matrix of TESTING.md, proposal P3.
      4 #
      5 # Why this job exists, concretely:
      6 #
      7 #   MAX_DIGEST in src/microhttpd/digestauth.c is the size of the largest
      8 #   digest the build supports.  It is 16 in an MD5-only build and 32 when
      9 #   SHA-256 or SHA-512/256 are compiled in.  The out-of-bounds stack
     10 #   write fixed in commit 5a73c1ae wrote up to 64 bytes into a
     11 #   MAX_DIGEST-sized array, so *the very same request overflows by a
     12 #   different amount - or not at all - depending purely on the configure
     13 #   flags*.  A CI that only ever builds the default configuration cannot
     14 #   see that, and cannot tell whether a fix holds in every shipped build.
     15 #   Distributions do ship reduced builds, so the reduced builds have to
     16 #   be tested.
     17 #
     18 #   The same argument applies to --enable-asserts (mhd_assert() is
     19 #   compiled out of a normal build, so every invariant the library
     20 #   documents is unchecked there), to the sanitizers (they are the only
     21 #   oracle that turns a silent memory-safety defect into a test failure)
     22 #   and to -m32 (pointer and size_t width change the arithmetic in the
     23 #   read-buffer shift-back path of 29eaa56b).
     24 #
     25 # The sweep is
     26 #
     27 #   {asserts on, asserts off}
     28 #     x {sanitizers on, sanitizers off}
     29 #     x {all digests, MD5 only, SHA-256 only, SHA-512/256 only,
     30 #        --disable-dauth, --disable-bauth}
     31 #     x {native 64-bit, 32-bit via -m32}
     32 #
     33 # = 48 combinations, each a clean out-of-tree build plus a full
     34 # "make check".  This is a nightly job; the every-push subset is job
     35 # 2-test.
     36 #
     37 # Combinations that the environment cannot support (no 32-bit toolchain,
     38 # no sanitizer runtime, no 32-bit sanitizer runtime) are reported as SKIP
     39 # with the reason, never as a failure - a missing i386 libasan is a
     40 # property of the runner, not a defect in MHD.
     41 #
     42 # Knobs:
     43 #   MHD_CI_MATRIX_FILTER   only run combinations whose tag matches this
     44 #                          extended regular expression
     45 #   MHD_CI_MATRIX_JOBS     -j level for each build (default: nproc)
     46 #   MHD_CI_MATRIX_KEEP     set to 1 to keep the build trees for triage
     47 #
     48 # No "set -e": a failing combination must be recorded and the sweep must
     49 # continue.  No "set -x" either (job.sh switches it on): with 48 builds
     50 # the trace drowns the summary, and every step prints its own banner.
     51 set +x
     52 set -uo pipefail
     53 
     54 srcdir="$(pwd)"
     55 probe_dir="$(mktemp -d)"
     56 trap 'rm -rf "${probe_dir}"' EXIT
     57 
     58 jobs="${MHD_CI_MATRIX_JOBS:-$(nproc)}"
     59 filter="${MHD_CI_MATRIX_FILTER:-.}"
     60 keep="${MHD_CI_MATRIX_KEEP:-0}"
     61 
     62 # ---------------------------------------------------------------------
     63 # Environment probes
     64 # ---------------------------------------------------------------------
     65 
     66 # Compile *and run* a trivial program.  Running it matters: a machine can
     67 # have a working 32-bit compiler and 32-bit headers while being unable to
     68 # execute i386 binaries at all (missing loader, restricted container,
     69 # CONFIG_IA32_EMULATION off).  Probing with a compile alone would then
     70 # turn every 32-bit combination into a wall of bogus SIGSEGV failures
     71 # instead of one honest SKIP.
     72 probe_cc()
     73 {
     74 	printf 'int main(void){return 0;}\n' > "${probe_dir}/probe.c"
     75 	# shellcheck disable=SC2086
     76 	${CC:-gcc} "$@" -o "${probe_dir}/probe" "${probe_dir}/probe.c" \
     77 		> "${probe_dir}/probe.log" 2>&1 || return 1
     78 	"${probe_dir}/probe" >> "${probe_dir}/probe.log" 2>&1
     79 }
     80 
     81 have_m32=no
     82 have_san=no
     83 have_m32_san=no
     84 have_m32_curl=no
     85 have_m32_gnutls=no
     86 
     87 probe_cc -m32                            && have_m32=yes
     88 probe_cc -fsanitize=address,undefined    && have_san=yes
     89 if [ "${have_m32}" = "yes" ]; then
     90 	probe_cc -m32 -fsanitize=address,undefined && have_m32_san=yes
     91 	probe_cc -m32 -lcurl                       && have_m32_curl=yes
     92 	probe_cc -m32 -lgnutls                     && have_m32_gnutls=yes
     93 fi
     94 
     95 echo "== environment probes =============================================="
     96 echo "  gcc -m32 .............................. ${have_m32}"
     97 echo "  gcc -fsanitize=address,undefined ...... ${have_san}"
     98 echo "  gcc -m32 -fsanitize=address,undefined . ${have_m32_san}"
     99 echo "  32-bit libcurl ........................ ${have_m32_curl}"
    100 echo "  32-bit gnutls ......................... ${have_m32_gnutls}"
    101 echo "===================================================================="
    102 
    103 # ---------------------------------------------------------------------
    104 # The axes.  Every option spelling below is the one configure.ac really
    105 # defines; --enable-md5 and --enable-sha256 take a TYPE argument
    106 # (yes/no/builtin/tlslib) while --enable-sha512-256 is a plain
    107 # enable/disable switch, and --enable-sanitizers takes a comma separated
    108 # list.
    109 # ---------------------------------------------------------------------
    110 
    111 # tag                 configure arguments
    112 digest_tags=(all md5 sha256 sha512-256 nodauth nobauth)
    113 digest_args_all=""
    114 digest_args_md5="--enable-md5=builtin --enable-sha256=no --disable-sha512-256"
    115 digest_args_sha256="--enable-md5=no --enable-sha256=builtin --disable-sha512-256"
    116 digest_args_sha512_256="--enable-md5=no --enable-sha256=no --enable-sha512-256"
    117 digest_args_nodauth="--disable-dauth"
    118 digest_args_nobauth="--disable-bauth"
    119 
    120 digest_args()
    121 {
    122 	case "$1" in
    123 		all)         echo "${digest_args_all}" ;;
    124 		md5)         echo "${digest_args_md5}" ;;
    125 		sha256)      echo "${digest_args_sha256}" ;;
    126 		sha512-256)  echo "${digest_args_sha512_256}" ;;
    127 		nodauth)     echo "${digest_args_nodauth}" ;;
    128 		nobauth)     echo "${digest_args_nobauth}" ;;
    129 	esac
    130 }
    131 
    132 results=()
    133 failures=0
    134 
    135 record()
    136 {
    137 	results+=("$(printf '%-34s %-5s %s' "$1" "$2" "${3:-}")")
    138 }
    139 
    140 run_combination()
    141 {
    142 	local tag="$1" bits="$2" asserts="$3" san="$4" digest="$5"
    143 	local builddir="${srcdir}/build-${tag}"
    144 	local -a args=()
    145 
    146 	if ! printf '%s' "${tag}" | grep -Eq -- "${filter}"; then
    147 		return 0
    148 	fi
    149 
    150 	# --- skip decisions -------------------------------------------
    151 	if [ "${bits}" = "32" ] && [ "${have_m32}" != "yes" ]; then
    152 		record "${tag}" SKIP "no 32-bit toolchain (gcc -m32 fails)"
    153 		return 0
    154 	fi
    155 	if [ "${san}" = "on" ] && [ "${bits}" = "64" ] && [ "${have_san}" != "yes" ]; then
    156 		record "${tag}" SKIP "no sanitizer runtime for this compiler"
    157 		return 0
    158 	fi
    159 	if [ "${san}" = "on" ] && [ "${bits}" = "32" ] && [ "${have_m32_san}" != "yes" ]; then
    160 		record "${tag}" SKIP "no 32-bit sanitizer runtime (i386 libasan)"
    161 		return 0
    162 	fi
    163 
    164 	# --- configure arguments --------------------------------------
    165 	if [ "${asserts}" = "on" ]; then
    166 		args+=(--enable-asserts)
    167 	else
    168 		args+=(--disable-asserts)
    169 	fi
    170 	if [ "${san}" = "on" ]; then
    171 		# shellcheck disable=SC2054  # one argument, the comma is part of it
    172 		args+=(--enable-sanitizers=address,undefined)
    173 	fi
    174 	# Word splitting is intended here: digest_args() returns a
    175 	# whitespace separated list of configure arguments.
    176 	# shellcheck disable=SC2207
    177 	args+=($(digest_args "${digest}"))
    178 
    179 	if [ "${bits}" = "32" ]; then
    180 		args+=(CC="${CC:-gcc} -m32")
    181 		args+=(--build=x86_64-pc-linux-gnu --host=i686-pc-linux-gnu)
    182 		# The 32-bit development libraries of libcurl and GnuTLS are
    183 		# usually not installed next to a 64-bit toolchain; turn the
    184 		# corresponding parts of the suite off explicitly rather than
    185 		# letting configure fail a link test and print a warning.
    186 		[ "${have_m32_curl}" = "yes" ]   || args+=(--disable-curl)
    187 		[ "${have_m32_gnutls}" = "yes" ] || args+=(--disable-https)
    188 		# src/examples/json_echo links against libjansson, which is
    189 		# not part of MHD's own dependency set and is essentially
    190 		# never installed for i386 next to an amd64 toolchain.  The
    191 		# examples are not built by "make check", so dropping them
    192 		# costs no coverage and keeps the 32-bit leg buildable.
    193 		args+=(--disable-examples)
    194 	fi
    195 
    196 	# --- clean out-of-tree build ----------------------------------
    197 	rm -rf "${builddir}"
    198 	mkdir -p "${builddir}"
    199 
    200 	echo "===================================================================="
    201 	echo "== ${tag}"
    202 	echo "==   ../configure ${args[*]}"
    203 	echo "===================================================================="
    204 
    205 	if ! ( cd "${builddir}" && ../configure "${args[@]}" ); then
    206 		record "${tag}" FAIL "configure failed"
    207 		failures=$((failures + 1))
    208 		return 0
    209 	fi
    210 	if ! ( cd "${builddir}" && make -j"${jobs}" ); then
    211 		record "${tag}" FAIL "build failed"
    212 		failures=$((failures + 1))
    213 		return 0
    214 	fi
    215 	if ! ( cd "${builddir}" && make -j"${jobs}" check ); then
    216 		record "${tag}" FAIL "make check failed"
    217 		failures=$((failures + 1))
    218 		echo "## test logs for ${tag}:"
    219 		find "${builddir}" -name 'test-suite.log' -print -exec cat {} \; || true
    220 		return 0
    221 	fi
    222 
    223 	record "${tag}" PASS ""
    224 	[ "${keep}" = "1" ] || rm -rf "${builddir}"
    225 	return 0
    226 }
    227 
    228 for bits in 64 32; do
    229 	for asserts in on off; do
    230 		for san in on off; do
    231 			for digest in "${digest_tags[@]}"; do
    232 				run_combination \
    233 					"${bits}bit-asserts-${asserts}-san-${san}-${digest}" \
    234 					"${bits}" "${asserts}" "${san}" "${digest}"
    235 			done
    236 		done
    237 	done
    238 done
    239 
    240 # ---------------------------------------------------------------------
    241 # Summary
    242 # ---------------------------------------------------------------------
    243 echo
    244 echo "===================================================================="
    245 echo "== build-configuration matrix summary"
    246 echo "===================================================================="
    247 printf '%-34s %-5s %s\n' "COMBINATION" "STATE" "NOTE"
    248 for line in "${results[@]}"; do
    249 	echo "${line}"
    250 done
    251 echo "===================================================================="
    252 echo "== ${failures} failing combination(s)"
    253 echo "===================================================================="
    254 
    255 if [ "${failures}" -ne 0 ]; then
    256 	exit 1
    257 fi
    258 exit 0