#!/bin/bash -u # Copyright 2016 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ################################################################################ # Percentage threshold that needs to be reached for marking a build as failed. ALLOWED_BROKEN_TARGETS_PERCENTAGE=${ALLOWED_BROKEN_TARGETS_PERCENTAGE:-10} # Test all fuzz targets in the $OUT/ dir. TOTAL_TARGETS_COUNT=0 # Number of CPUs available, this is needed for running tests in parallel. NPROC=$(nproc) # Directories where bad build check results will be written to. VALID_TARGETS_DIR="/tmp/valid_fuzz_targets" BROKEN_TARGETS_DIR="/tmp/broken_fuzz_targets" rm -rf $VALID_TARGETS_DIR rm -rf $BROKEN_TARGETS_DIR mkdir $VALID_TARGETS_DIR mkdir $BROKEN_TARGETS_DIR # Main loop that iterates through all fuzz targets and runs the check. for FUZZER_BINARY in $(find $OUT/ -maxdepth 1 -executable -type f); do if file "$FUZZER_BINARY" | grep -v ELF > /dev/null 2>&1; then continue fi # Continue if not a fuzz target. if [[ $FUZZING_ENGINE != "none" ]]; then grep "LLVMFuzzerTestOneInput" $FUZZER_BINARY > /dev/null 2>&1 || continue fi FUZZER=$(basename $FUZZER_BINARY) if [[ "$FUZZER" == afl-* ]]; then continue fi if [[ "$FUZZER" == honggfuzz ]]; then continue fi echo "INFO: performing bad build checks for $FUZZER_BINARY." LOG_PATH_FOR_BROKEN_TARGET="${BROKEN_TARGETS_DIR}/${FUZZER}" # Launch bad build check process in the background. Ignore the exit codes, as # we check the percentage of broken fuzz targets after running all the checks. bad_build_check $FUZZER_BINARY &> $LOG_PATH_FOR_BROKEN_TARGET & # Count total number of fuzz targets being tested. TOTAL_TARGETS_COUNT=$[$TOTAL_TARGETS_COUNT+1] # Do not spawn more processes than the number of CPUs available. n_child_proc=$(jobs -rp | wc -l) while [ "$n_child_proc" -eq "$NPROC" ]; do sleep 4 n_child_proc=$(jobs -rp | wc -l) done done # Wait for background processes to finish. wait # Sanity check in case there are no fuzz targets in the $OUT/ dir. if [ "$TOTAL_TARGETS_COUNT" -eq "0" ]; then echo "ERROR: no fuzzers found in $OUT/" ls -al $OUT exit 1 fi # An empty log file indicated that corresponding fuzz target is not broken. find $BROKEN_TARGETS_DIR -empty -exec mv {} $VALID_TARGETS_DIR \; # Calculate number of valid and broken fuzz targets. VALID_TARGETS_COUNT=$(ls $VALID_TARGETS_DIR | wc -l) BROKEN_TARGETS_COUNT=$(ls $BROKEN_TARGETS_DIR | wc -l) # Sanity check to make sure that bad build check doesn't skip any fuzz target. if [ "$TOTAL_TARGETS_COUNT" -ne "$[$VALID_TARGETS_COUNT+$BROKEN_TARGETS_COUNT]" ]; then echo "ERROR: bad_build_check seems to have a bug, total number of fuzz" \ "does not match number of fuzz targets tested." echo "Total fuzz targets ($TOTAL_TARGETS_COUNT):" ls -al $OUT echo "Valid fuzz targets ($VALID_TARGETS_COUNT):" ls -al $VALID_TARGETS_DIR echo "Total fuzz targets ($BROKEN_TARGETS_COUNT):" ls -al $BROKEN_TARGETS_DIR exit 1 fi # Build info about all broken fuzz targets (if any). if [ "$BROKEN_TARGETS_COUNT" -gt "0" ]; then echo "Broken fuzz targets ($BROKEN_TARGETS_COUNT):" for target in $(ls $BROKEN_TARGETS_DIR); do echo "${target}:" cat ${BROKEN_TARGETS_DIR}/${target} done fi # Calculate the percentage of broken fuzz targets and make the finel decision. BROKEN_TARGETS_PERCENTAGE=$[$BROKEN_TARGETS_COUNT*100/$TOTAL_TARGETS_COUNT] if [ "$BROKEN_TARGETS_PERCENTAGE" -gt "$ALLOWED_BROKEN_TARGETS_PERCENTAGE" ]; then echo "ERROR: $BROKEN_TARGETS_PERCENTAGE% of fuzz targets seem to be broken." \ "See the list above for a detailed information." # TODO: figure out how to not fail the "special" cases handled below. Those # are from "example" and "c-ares" projects and are too small targets to pass. if [ "$(ls $OUT/do_stuff_fuzzer $OUT/ares_*_fuzzer $OUT/checksum_fuzzer $OUT/xmltest $OUT/fuzz_compression_sas_rle 2>/dev/null | wc -l)" -gt "0" ]; then exit 0 fi exit 1 else echo "$TOTAL_TARGETS_COUNT fuzzers total, $BROKEN_TARGETS_COUNT seem to be" \ "broken ($BROKEN_TARGETS_PERCENTAGE%)." exit 0 fi