Skip to content

Commit eddcbfb

Browse files
author
MarcoFalke
committed
Merge #18166: ci: Run fuzz testing test cases (bitcoin-core/qa-assets) under valgrind to catch memory errors
f2472f6 tests: Improve test runner output in case of target errors (practicalswift) 733bbec tests: Add --exclude integer,parse_iso8601 (temporarily) to make Travis pass until uninitialized read issue in FormatISO8601DateTime is fixed (practicalswift) 5ea8144 tests: Add support for excluding fuzz targets using -x/--exclude (practicalswift) 555236f tests: Remove -detect_leaks=0 from test/fuzz/test_runner.py - no longer needed (practicalswift) a3b539a 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 f2472f6 👼 Tree-SHA512: bb0879d40167cf6906bc0ed31bed39db83c39c7beb46026f7b0ee53f28ff0526ad6fabc3f4cb3f5f18d3b8cafdcbf5f30105b35919f4e83697c71e838ed71493
2 parents 68e841e + f2472f6 commit eddcbfb

File tree

4 files changed

+51
-7
lines changed

4 files changed

+51
-7
lines changed

.travis.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ jobs:
134134
env: >-
135135
FILE_ENV="./ci/test/00_setup_env_native_fuzz.sh"
136136
137+
- stage: test
138+
name: 'x86_64 Linux [GOAL: install] [bionic] [no depends, only system libs, fuzzers under valgrind]'
139+
env: >-
140+
FILE_ENV="./ci/test/00_setup_env_native_fuzz_with_valgrind.sh"
141+
137142
- stage: test
138143
name: 'x86_64 Linux [GOAL: install] [bionic] [no wallet]'
139144
env: >-
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Copyright (c) 2019 The Bitcoin Core developers
4+
# Distributed under the MIT software license, see the accompanying
5+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
6+
7+
export LC_ALL=C.UTF-8
8+
9+
export CONTAINER_NAME=ci_native_fuzz_valgrind
10+
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"
11+
export NO_DEPENDS=1
12+
export RUN_UNIT_TESTS=false
13+
export RUN_FUNCTIONAL_TESTS=false
14+
export RUN_FUZZ_TESTS=true
15+
export FUZZ_TESTS_CONFIG="--exclude integer,parse_iso8601 --valgrind"
16+
export GOAL="install"
17+
export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer CC=clang-8 CXX=clang++-8"
18+
# Use clang-8, instead of default clang on bionic, which is clang-6 and does not come with libfuzzer on aarch64

ci/test/06_script_b.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ fi
3636

3737
if [ "$RUN_FUZZ_TESTS" = "true" ]; then
3838
BEGIN_FOLD fuzz-tests
39-
DOCKER_EXEC test/fuzz/test_runner.py -l DEBUG ${DIR_FUZZ_IN}
39+
DOCKER_EXEC test/fuzz/test_runner.py ${FUZZ_TESTS_CONFIG} -l DEBUG ${DIR_FUZZ_IN}
4040
END_FOLD
4141
fi

test/fuzz/test_runner.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"tx_out",
4848
]
4949

50+
5051
def main():
5152
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
5253
parser.add_argument(
@@ -64,7 +65,12 @@ def main():
6465
parser.add_argument(
6566
'--valgrind',
6667
action='store_true',
67-
help='If true, run fuzzing binaries under the valgrind memory error detector. Valgrind 3.14 or later required.',
68+
help='If true, run fuzzing binaries under the valgrind memory error detector',
69+
)
70+
parser.add_argument(
71+
'-x',
72+
'--exclude',
73+
help="A comma-separated list of targets to exclude",
6874
)
6975
parser.add_argument(
7076
'seed_dir',
@@ -100,7 +106,7 @@ def main():
100106
logging.error("No fuzz targets found")
101107
sys.exit(1)
102108

103-
logging.info("Fuzz targets found: {}".format(test_list_all))
109+
logging.debug("{} fuzz target(s) found: {}".format(len(test_list_all), " ".join(sorted(test_list_all))))
104110

105111
args.target = args.target or test_list_all # By default run all
106112
test_list_error = list(set(args.target).difference(set(test_list_all)))
@@ -109,7 +115,15 @@ def main():
109115
test_list_selection = list(set(test_list_all).intersection(set(args.target)))
110116
if not test_list_selection:
111117
logging.error("No fuzz targets selected")
112-
logging.info("Fuzz targets selected: {}".format(test_list_selection))
118+
if args.exclude:
119+
for excluded_target in args.exclude.split(","):
120+
if excluded_target not in test_list_selection:
121+
logging.error("Target \"{}\" not found in current target list.".format(excluded_target))
122+
continue
123+
test_list_selection.remove(excluded_target)
124+
test_list_selection.sort()
125+
126+
logging.info("{} of {} detected fuzz target(s) selected: {}".format(len(test_list_selection), len(test_list_all), " ".join(test_list_selection)))
113127

114128
try:
115129
help_output = subprocess.run(
@@ -146,16 +160,23 @@ def run_once(*, corpus, test_list, build_dir, export_coverage, use_valgrind):
146160
args = [
147161
os.path.join(build_dir, 'src', 'test', 'fuzz', t),
148162
'-runs=1',
149-
'-detect_leaks=0',
150163
corpus_path,
151164
]
152165
if use_valgrind:
153-
args = ['valgrind', '--quiet', '--error-exitcode=1', '--exit-on-first-error=yes'] + args
166+
args = ['valgrind', '--quiet', '--error-exitcode=1'] + args
154167
logging.debug('Run {} with args {}'.format(t, args))
155168
result = subprocess.run(args, stderr=subprocess.PIPE, universal_newlines=True)
156169
output = result.stderr
157170
logging.debug('Output: {}'.format(output))
158-
result.check_returncode()
171+
try:
172+
result.check_returncode()
173+
except subprocess.CalledProcessError as e:
174+
if e.stdout:
175+
logging.info(e.stdout)
176+
if e.stderr:
177+
logging.info(e.stderr)
178+
logging.info("Target \"{}\" failed with exit code {}: {}".format(t, e.returncode, " ".join(args)))
179+
sys.exit(1)
159180
if not export_coverage:
160181
continue
161182
for l in output.splitlines():

0 commit comments

Comments
 (0)