aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcoFalke <falke.marco@gmail.com>2020-02-19 08:20:27 -0800
committerMarcoFalke <falke.marco@gmail.com>2020-02-19 08:20:38 -0800
commiteddcbfb109e592fdf39ea2c38c4d7ba183de3e34 (patch)
tree1dbaa807d15d8fa11e7797a01a532999c175e18f
parent68e841e0af223a220d1f037e4c5680c1b228aa3e (diff)
parentf2472f64604a0c583f950c56e8753d0bee246388 (diff)
Merge #18166: ci: Run fuzz testing test cases (bitcoin-core/qa-assets) under valgrind to catch memory errors
f2472f64604a0c583f950c56e8753d0bee246388 tests: Improve test runner output in case of target errors (practicalswift) 733bbec34fbec85574cc456832b2b2f807e5dce9 tests: Add --exclude integer,parse_iso8601 (temporarily) to make Travis pass until uninitialized read issue in FormatISO8601DateTime is fixed (practicalswift) 5ea81449f30a6fe6db3b6df5e8009f21a782ff44 tests: Add support for excluding fuzz targets using -x/--exclude (practicalswift) 555236f769c13518db70f5df36e5688d63486bd5 tests: Remove -detect_leaks=0 from test/fuzz/test_runner.py - no longer needed (practicalswift) a3b539a924f8611abb3096f2bd9d35094b5577e3 ci: Run fuzz testing test cases under valgrind (practicalswift) Pull request description: Run fuzz testing [test cases (bitcoin-core/qa-assets)](https://github.com/bitcoin-core/qa-assets) under `valgrind`. This would have caught `util: Avoid potential uninitialized read in FormatISO8601DateTime(int64_t) by checking gmtime_s/gmtime_r return value` (#18162) and similar cases. ACKs for top commit: MarcoFalke: ACK f2472f64604a0c583f950c56e8753d0bee246388 👼 Tree-SHA512: bb0879d40167cf6906bc0ed31bed39db83c39c7beb46026f7b0ee53f28ff0526ad6fabc3f4cb3f5f18d3b8cafdcbf5f30105b35919f4e83697c71e838ed71493
-rw-r--r--.travis.yml5
-rw-r--r--ci/test/00_setup_env_native_fuzz_with_valgrind.sh18
-rwxr-xr-xci/test/06_script_b.sh2
-rwxr-xr-xtest/fuzz/test_runner.py33
4 files changed, 51 insertions, 7 deletions
diff --git a/.travis.yml b/.travis.yml
index c5b8c476b7..9a111c03ad 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -135,6 +135,11 @@ jobs:
FILE_ENV="./ci/test/00_setup_env_native_fuzz.sh"
- stage: test
+ name: 'x86_64 Linux [GOAL: install] [bionic] [no depends, only system libs, fuzzers under valgrind]'
+ env: >-
+ FILE_ENV="./ci/test/00_setup_env_native_fuzz_with_valgrind.sh"
+
+ - stage: test
name: 'x86_64 Linux [GOAL: install] [bionic] [no wallet]'
env: >-
FILE_ENV="./ci/test/00_setup_env_native_nowallet.sh"
diff --git a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
new file mode 100644
index 0000000000..6e1c400d50
--- /dev/null
+++ b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+export LC_ALL=C.UTF-8
+
+export CONTAINER_NAME=ci_native_fuzz_valgrind
+export PACKAGES="clang-8 llvm-8 python3 libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev valgrind"
+export NO_DEPENDS=1
+export RUN_UNIT_TESTS=false
+export RUN_FUNCTIONAL_TESTS=false
+export RUN_FUZZ_TESTS=true
+export FUZZ_TESTS_CONFIG="--exclude integer,parse_iso8601 --valgrind"
+export GOAL="install"
+export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer CC=clang-8 CXX=clang++-8"
+# Use clang-8, instead of default clang on bionic, which is clang-6 and does not come with libfuzzer on aarch64
diff --git a/ci/test/06_script_b.sh b/ci/test/06_script_b.sh
index 537493a710..3b32513353 100755
--- a/ci/test/06_script_b.sh
+++ b/ci/test/06_script_b.sh
@@ -36,6 +36,6 @@ fi
if [ "$RUN_FUZZ_TESTS" = "true" ]; then
BEGIN_FOLD fuzz-tests
- DOCKER_EXEC test/fuzz/test_runner.py -l DEBUG ${DIR_FUZZ_IN}
+ DOCKER_EXEC test/fuzz/test_runner.py ${FUZZ_TESTS_CONFIG} -l DEBUG ${DIR_FUZZ_IN}
END_FOLD
fi
diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py
index 61c3e700c5..a56651cca5 100755
--- a/test/fuzz/test_runner.py
+++ b/test/fuzz/test_runner.py
@@ -47,6 +47,7 @@ FUZZERS_MISSING_CORPORA = [
"tx_out",
]
+
def main():
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument(
@@ -64,7 +65,12 @@ def main():
parser.add_argument(
'--valgrind',
action='store_true',
- help='If true, run fuzzing binaries under the valgrind memory error detector. Valgrind 3.14 or later required.',
+ help='If true, run fuzzing binaries under the valgrind memory error detector',
+ )
+ parser.add_argument(
+ '-x',
+ '--exclude',
+ help="A comma-separated list of targets to exclude",
)
parser.add_argument(
'seed_dir',
@@ -100,7 +106,7 @@ def main():
logging.error("No fuzz targets found")
sys.exit(1)
- logging.info("Fuzz targets found: {}".format(test_list_all))
+ logging.debug("{} fuzz target(s) found: {}".format(len(test_list_all), " ".join(sorted(test_list_all))))
args.target = args.target or test_list_all # By default run all
test_list_error = list(set(args.target).difference(set(test_list_all)))
@@ -109,7 +115,15 @@ def main():
test_list_selection = list(set(test_list_all).intersection(set(args.target)))
if not test_list_selection:
logging.error("No fuzz targets selected")
- logging.info("Fuzz targets selected: {}".format(test_list_selection))
+ if args.exclude:
+ for excluded_target in args.exclude.split(","):
+ if excluded_target not in test_list_selection:
+ logging.error("Target \"{}\" not found in current target list.".format(excluded_target))
+ continue
+ test_list_selection.remove(excluded_target)
+ test_list_selection.sort()
+
+ logging.info("{} of {} detected fuzz target(s) selected: {}".format(len(test_list_selection), len(test_list_all), " ".join(test_list_selection)))
try:
help_output = subprocess.run(
@@ -146,16 +160,23 @@ def run_once(*, corpus, test_list, build_dir, export_coverage, use_valgrind):
args = [
os.path.join(build_dir, 'src', 'test', 'fuzz', t),
'-runs=1',
- '-detect_leaks=0',
corpus_path,
]
if use_valgrind:
- args = ['valgrind', '--quiet', '--error-exitcode=1', '--exit-on-first-error=yes'] + args
+ args = ['valgrind', '--quiet', '--error-exitcode=1'] + args
logging.debug('Run {} with args {}'.format(t, args))
result = subprocess.run(args, stderr=subprocess.PIPE, universal_newlines=True)
output = result.stderr
logging.debug('Output: {}'.format(output))
- result.check_returncode()
+ try:
+ result.check_returncode()
+ except subprocess.CalledProcessError as e:
+ if e.stdout:
+ logging.info(e.stdout)
+ if e.stderr:
+ logging.info(e.stderr)
+ logging.info("Target \"{}\" failed with exit code {}: {}".format(t, e.returncode, " ".join(args)))
+ sys.exit(1)
if not export_coverage:
continue
for l in output.splitlines():