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