1ced56a00Sopenharmony_ci#!/bin/bash
2ced56a00Sopenharmony_ci# SPDX-License-Identifier: MIT
3ced56a00Sopenharmony_ci# Copyright 2020 Google LLC
4ced56a00Sopenharmony_ci#
5ced56a00Sopenharmony_ci# Use of this source code is governed by an MIT-style
6ced56a00Sopenharmony_ci# license that can be found in the LICENSE file or at
7ced56a00Sopenharmony_ci# https://opensource.org/licenses/MIT.
8ced56a00Sopenharmony_ci#
9ced56a00Sopenharmony_ci#
10ced56a00Sopenharmony_ci# Test script for fsverity-utils.  Runs 'make check' in lots of configurations,
11ced56a00Sopenharmony_ci# runs static analysis, and does a few other tests.
12ced56a00Sopenharmony_ci#
13ced56a00Sopenharmony_ci# Note: for more test coverage, in addition to running this script, also build
14ced56a00Sopenharmony_ci# fsverity-utils into a kvm-xfstests test appliance and run
15ced56a00Sopenharmony_ci# 'kvm-xfstests -c ext4,f2fs -g verity'
16ced56a00Sopenharmony_ci
17ced56a00Sopenharmony_ciset -e -u -o pipefail
18ced56a00Sopenharmony_cicd "$(dirname "$0")/.."
19ced56a00Sopenharmony_ci
20ced56a00Sopenharmony_cilog()
21ced56a00Sopenharmony_ci{
22ced56a00Sopenharmony_ci	echo "[$(date)] $*" 1>&2
23ced56a00Sopenharmony_ci}
24ced56a00Sopenharmony_ci
25ced56a00Sopenharmony_cifail()
26ced56a00Sopenharmony_ci{
27ced56a00Sopenharmony_ci	echo "FAIL: $*" 1>&2
28ced56a00Sopenharmony_ci	exit 1
29ced56a00Sopenharmony_ci}
30ced56a00Sopenharmony_ci
31ced56a00Sopenharmony_ciTMPDIR=$(mktemp -d -t libfsverity_test.XXXXXXXXX)
32ced56a00Sopenharmony_citrap 'rm -r "$TMPDIR"' EXIT
33ced56a00Sopenharmony_ci
34ced56a00Sopenharmony_ci# Both stdout and stderr go to log file.
35ced56a00Sopenharmony_ci# Only stderr goes to terminal.
36ced56a00Sopenharmony_ciecho "Starting fsverity-utils tests.  See run-tests.log for full output."
37ced56a00Sopenharmony_cirm -f run-tests.log
38ced56a00Sopenharmony_ciexec >> run-tests.log
39ced56a00Sopenharmony_ciexec 2> >(tee -ia run-tests.log 1>&2)
40ced56a00Sopenharmony_ci
41ced56a00Sopenharmony_ciMAKE="make -j$(getconf _NPROCESSORS_ONLN)"
42ced56a00Sopenharmony_ci
43ced56a00Sopenharmony_ciTEST_FUNCS=()
44ced56a00Sopenharmony_ci
45ced56a00Sopenharmony_cistatic_linking_test()
46ced56a00Sopenharmony_ci{
47ced56a00Sopenharmony_ci	log "Build and test with statically linking"
48ced56a00Sopenharmony_ci	$MAKE CFLAGS="-Werror"
49ced56a00Sopenharmony_ci	if ldd fsverity | grep libfsverity.so; then
50ced56a00Sopenharmony_ci		fail "fsverity binary should be statically linked to libfsverity by default"
51ced56a00Sopenharmony_ci	fi
52ced56a00Sopenharmony_ci	./fsverity --version
53ced56a00Sopenharmony_ci
54ced56a00Sopenharmony_ci	log "Check that all global symbols are prefixed with \"libfsverity_\""
55ced56a00Sopenharmony_ci	if nm libfsverity.a | grep ' T ' | grep -v " libfsverity_"; then
56ced56a00Sopenharmony_ci		fail "Some global symbols are not prefixed with \"libfsverity_\""
57ced56a00Sopenharmony_ci	fi
58ced56a00Sopenharmony_ci}
59ced56a00Sopenharmony_ciTEST_FUNCS+=(static_linking_test)
60ced56a00Sopenharmony_ci
61ced56a00Sopenharmony_cidynamic_linking_test()
62ced56a00Sopenharmony_ci{
63ced56a00Sopenharmony_ci	log "Build and test with dynamic linking"
64ced56a00Sopenharmony_ci	$MAKE CFLAGS="-Werror" USE_SHARED_LIB=1 check
65ced56a00Sopenharmony_ci	if ! ldd fsverity | grep libfsverity.so; then
66ced56a00Sopenharmony_ci		fail "fsverity binary should be dynamically linked to libfsverity when USE_SHARED_LIB=1"
67ced56a00Sopenharmony_ci	fi
68ced56a00Sopenharmony_ci
69ced56a00Sopenharmony_ci	log "Check that all exported symbols are prefixed with \"libfsverity_\""
70ced56a00Sopenharmony_ci	if nm libfsverity.so | grep ' T ' | grep -v " libfsverity_"; then
71ced56a00Sopenharmony_ci		fail "Some exported symbols are not prefixed with \"libfsverity_\""
72ced56a00Sopenharmony_ci	fi
73ced56a00Sopenharmony_ci}
74ced56a00Sopenharmony_ciTEST_FUNCS+=(dynamic_linking_test)
75ced56a00Sopenharmony_ci
76ced56a00Sopenharmony_cicplusplus_test()
77ced56a00Sopenharmony_ci{
78ced56a00Sopenharmony_ci	$MAKE CFLAGS="-Werror" libfsverity.so
79ced56a00Sopenharmony_ci	log "Test using libfsverity from C++ program"
80ced56a00Sopenharmony_ci	cat > "$TMPDIR/test.cc" <<EOF
81ced56a00Sopenharmony_ci#include <libfsverity.h>
82ced56a00Sopenharmony_ci#include <iostream>
83ced56a00Sopenharmony_ciint main()
84ced56a00Sopenharmony_ci{
85ced56a00Sopenharmony_ci	std::cout << libfsverity_get_digest_size(FS_VERITY_HASH_ALG_SHA256) << std::endl;
86ced56a00Sopenharmony_ci}
87ced56a00Sopenharmony_ciEOF
88ced56a00Sopenharmony_ci	c++ -Wall -Werror "$TMPDIR/test.cc" -Iinclude -L. -lfsverity -o "$TMPDIR/test"
89ced56a00Sopenharmony_ci	[ "$(LD_LIBRARY_PATH=. "$TMPDIR/test")" = "32" ]
90ced56a00Sopenharmony_ci	rm "${TMPDIR:?}"/*
91ced56a00Sopenharmony_ci}
92ced56a00Sopenharmony_ciTEST_FUNCS+=(cplusplus_test)
93ced56a00Sopenharmony_ci
94ced56a00Sopenharmony_ciuntracked_files_test()
95ced56a00Sopenharmony_ci{
96ced56a00Sopenharmony_ci	log "Check that build doesn't produce untracked files"
97ced56a00Sopenharmony_ci	$MAKE CFLAGS="-Werror" all test_programs
98ced56a00Sopenharmony_ci	if git status --short | grep -q '^??'; then
99ced56a00Sopenharmony_ci		git status
100ced56a00Sopenharmony_ci		fail "Build produced untracked files (check 'git status').  Missing gitignore entry?"
101ced56a00Sopenharmony_ci	fi
102ced56a00Sopenharmony_ci}
103ced56a00Sopenharmony_ciTEST_FUNCS+=(untracked_files_test)
104ced56a00Sopenharmony_ci
105ced56a00Sopenharmony_ciuninstall_test()
106ced56a00Sopenharmony_ci{
107ced56a00Sopenharmony_ci	log "Test that 'make uninstall' uninstalls all files"
108ced56a00Sopenharmony_ci	make DESTDIR="$TMPDIR" install
109ced56a00Sopenharmony_ci	if [ "$(find "$TMPDIR" -type f -o -type l | wc -l)" = 0 ]; then
110ced56a00Sopenharmony_ci		fail "'make install' didn't install any files"
111ced56a00Sopenharmony_ci	fi
112ced56a00Sopenharmony_ci	make DESTDIR="$TMPDIR" uninstall
113ced56a00Sopenharmony_ci	if [ "$(find "$TMPDIR" -type f -o -type l | wc -l)" != 0 ]; then
114ced56a00Sopenharmony_ci		fail "'make uninstall' didn't uninstall all files"
115ced56a00Sopenharmony_ci	fi
116ced56a00Sopenharmony_ci	rm -r "${TMPDIR:?}"/*
117ced56a00Sopenharmony_ci}
118ced56a00Sopenharmony_ciTEST_FUNCS+=(uninstall_test)
119ced56a00Sopenharmony_ci
120ced56a00Sopenharmony_cidash_test()
121ced56a00Sopenharmony_ci{
122ced56a00Sopenharmony_ci	log "Build, install, and uninstall with dash"
123ced56a00Sopenharmony_ci	make clean SHELL=/bin/dash
124ced56a00Sopenharmony_ci	make DESTDIR="$TMPDIR" SHELL=/bin/dash install
125ced56a00Sopenharmony_ci	make DESTDIR="$TMPDIR" SHELL=/bin/dash uninstall
126ced56a00Sopenharmony_ci}
127ced56a00Sopenharmony_ciTEST_FUNCS+=(dash_test)
128ced56a00Sopenharmony_ci
129ced56a00Sopenharmony_cilicense_test()
130ced56a00Sopenharmony_ci{
131ced56a00Sopenharmony_ci	log "Check that all files have license and copyright info"
132ced56a00Sopenharmony_ci	list="$TMPDIR/filelist"
133ced56a00Sopenharmony_ci	filter_license_info() {
134ced56a00Sopenharmony_ci		# files to exclude from license and copyright info checks
135ced56a00Sopenharmony_ci		grep -E -v '(\.gitignore|LICENSE|.*\.md|testdata|fsverity_uapi\.h|libfsverity\.pc\.in)'
136ced56a00Sopenharmony_ci	}
137ced56a00Sopenharmony_ci	git grep -L 'SPDX-License-Identifier: MIT' \
138ced56a00Sopenharmony_ci		| filter_license_info > "$list" || true
139ced56a00Sopenharmony_ci	if [ -s "$list" ]; then
140ced56a00Sopenharmony_ci		fail "The following files are missing an appropriate SPDX license identifier: $(<"$list")"
141ced56a00Sopenharmony_ci	fi
142ced56a00Sopenharmony_ci	# For now some people still prefer a free-form license statement, not just SPDX.
143ced56a00Sopenharmony_ci	git grep -L 'Use of this source code is governed by an MIT-style' \
144ced56a00Sopenharmony_ci		| filter_license_info > "$list" || true
145ced56a00Sopenharmony_ci	if [ -s "$list" ]; then
146ced56a00Sopenharmony_ci		fail "The following files are missing an appropriate license statement: $(<"$list")"
147ced56a00Sopenharmony_ci	fi
148ced56a00Sopenharmony_ci	git grep -L '\<Copyright\>' | filter_license_info > "$list" || true
149ced56a00Sopenharmony_ci	if [ -s "$list" ]; then
150ced56a00Sopenharmony_ci		fail "The following files are missing a copyright statement: $(<"$list")"
151ced56a00Sopenharmony_ci	fi
152ced56a00Sopenharmony_ci	rm "$list"
153ced56a00Sopenharmony_ci}
154ced56a00Sopenharmony_ciTEST_FUNCS+=(license_test)
155ced56a00Sopenharmony_ci
156ced56a00Sopenharmony_cigcc_test()
157ced56a00Sopenharmony_ci{
158ced56a00Sopenharmony_ci	log "Build and test with gcc (-O2)"
159ced56a00Sopenharmony_ci	$MAKE CC=gcc CFLAGS="-O2 -Werror" check
160ced56a00Sopenharmony_ci
161ced56a00Sopenharmony_ci	log "Build and test with gcc (-O3)"
162ced56a00Sopenharmony_ci	$MAKE CC=gcc CFLAGS="-O3 -Werror" check
163ced56a00Sopenharmony_ci}
164ced56a00Sopenharmony_ciTEST_FUNCS+=(gcc_test)
165ced56a00Sopenharmony_ci
166ced56a00Sopenharmony_ciclang_test()
167ced56a00Sopenharmony_ci{
168ced56a00Sopenharmony_ci	log "Build and test with clang (-O2)"
169ced56a00Sopenharmony_ci	$MAKE CC=clang CFLAGS="-O2 -Werror" check
170ced56a00Sopenharmony_ci
171ced56a00Sopenharmony_ci	log "Build and test with clang (-O3)"
172ced56a00Sopenharmony_ci	$MAKE CC=clang CFLAGS="-O3 -Werror" check
173ced56a00Sopenharmony_ci}
174ced56a00Sopenharmony_ciTEST_FUNCS+=(clang_test)
175ced56a00Sopenharmony_ci
176ced56a00Sopenharmony_ci32bit_test()
177ced56a00Sopenharmony_ci{
178ced56a00Sopenharmony_ci	log "Build and test with gcc (32-bit)"
179ced56a00Sopenharmony_ci	$MAKE CC=gcc CFLAGS="-O2 -Werror -m32" check
180ced56a00Sopenharmony_ci}
181ced56a00Sopenharmony_ciTEST_FUNCS+=(32bit_test)
182ced56a00Sopenharmony_ci
183ced56a00Sopenharmony_cisanitizers_test()
184ced56a00Sopenharmony_ci{
185ced56a00Sopenharmony_ci	log "Build and test with clang + UBSAN"
186ced56a00Sopenharmony_ci	$MAKE CC=clang \
187ced56a00Sopenharmony_ci		CFLAGS="-O2 -Werror -fsanitize=undefined -fno-sanitize-recover=undefined" \
188ced56a00Sopenharmony_ci		check
189ced56a00Sopenharmony_ci
190ced56a00Sopenharmony_ci	log "Build and test with clang + ASAN"
191ced56a00Sopenharmony_ci	$MAKE CC=clang \
192ced56a00Sopenharmony_ci		CFLAGS="-O2 -Werror -fsanitize=address -fno-sanitize-recover=address" \
193ced56a00Sopenharmony_ci		check
194ced56a00Sopenharmony_ci
195ced56a00Sopenharmony_ci	log "Build and test with clang + unsigned integer overflow sanitizer"
196ced56a00Sopenharmony_ci	$MAKE CC=clang \
197ced56a00Sopenharmony_ci		CFLAGS="-O2 -Werror -fsanitize=unsigned-integer-overflow -fno-sanitize-recover=unsigned-integer-overflow" \
198ced56a00Sopenharmony_ci		check
199ced56a00Sopenharmony_ci
200ced56a00Sopenharmony_ci	log "Build and test with clang + CFI"
201ced56a00Sopenharmony_ci	$MAKE CC=clang CFLAGS="-O2 -Werror -fsanitize=cfi -flto -fvisibility=hidden" \
202ced56a00Sopenharmony_ci		AR=llvm-ar check
203ced56a00Sopenharmony_ci}
204ced56a00Sopenharmony_ciTEST_FUNCS+=(sanitizers_test)
205ced56a00Sopenharmony_ci
206ced56a00Sopenharmony_civalgrind_test()
207ced56a00Sopenharmony_ci{
208ced56a00Sopenharmony_ci	log "Build and test with valgrind"
209ced56a00Sopenharmony_ci	$MAKE TEST_WRAPPER_PROG="valgrind --quiet --error-exitcode=100 --leak-check=full --errors-for-leak-kinds=all" \
210ced56a00Sopenharmony_ci		CFLAGS="-O2 -Werror" check
211ced56a00Sopenharmony_ci}
212ced56a00Sopenharmony_ciTEST_FUNCS+=(valgrind_test)
213ced56a00Sopenharmony_ci
214ced56a00Sopenharmony_ciboringssl_test()
215ced56a00Sopenharmony_ci{
216ced56a00Sopenharmony_ci	log "Build and test using BoringSSL instead of OpenSSL"
217ced56a00Sopenharmony_ci	log "-> Building BoringSSL"
218ced56a00Sopenharmony_ci	$MAKE boringssl
219ced56a00Sopenharmony_ci	log "-> Building fsverity-utils linked to BoringSSL"
220ced56a00Sopenharmony_ci	$MAKE CFLAGS="-O2 -Werror" LDFLAGS="-Lboringssl/build/crypto" \
221ced56a00Sopenharmony_ci		CPPFLAGS="-Iboringssl/include" LDLIBS="-lcrypto -lpthread" check
222ced56a00Sopenharmony_ci}
223ced56a00Sopenharmony_ciTEST_FUNCS+=(boringssl_test)
224ced56a00Sopenharmony_ci
225ced56a00Sopenharmony_ciopenssl1_test()
226ced56a00Sopenharmony_ci{
227ced56a00Sopenharmony_ci	log "Build and test using OpenSSL 1.0"
228ced56a00Sopenharmony_ci	$MAKE CFLAGS="-O2 -Werror" LDFLAGS="-L/usr/lib/openssl-1.0" \
229ced56a00Sopenharmony_ci		CPPFLAGS="-I/usr/include/openssl-1.0" check
230ced56a00Sopenharmony_ci}
231ced56a00Sopenharmony_ciTEST_FUNCS+=(openssl1_test)
232ced56a00Sopenharmony_ci
233ced56a00Sopenharmony_ciopenssl3_test()
234ced56a00Sopenharmony_ci{
235ced56a00Sopenharmony_ci	log "Build and test using OpenSSL 3.0"
236ced56a00Sopenharmony_ci	OSSL3=$HOME/src/openssl/inst/usr/local
237ced56a00Sopenharmony_ci	LD_LIBRARY_PATH="$OSSL3/lib64" $MAKE CFLAGS="-O2 -Werror" \
238ced56a00Sopenharmony_ci		LDFLAGS="-L$OSSL3/lib64" CPPFLAGS="-I$OSSL3/include" check
239ced56a00Sopenharmony_ci}
240ced56a00Sopenharmony_ciTEST_FUNCS+=(openssl3_test)
241ced56a00Sopenharmony_ci
242ced56a00Sopenharmony_ciunsigned_char_test()
243ced56a00Sopenharmony_ci{
244ced56a00Sopenharmony_ci	log "Build and test using -funsigned-char"
245ced56a00Sopenharmony_ci	$MAKE CFLAGS="-O2 -Werror -funsigned-char" check
246ced56a00Sopenharmony_ci}
247ced56a00Sopenharmony_ciTEST_FUNCS+=(unsigned_char_test)
248ced56a00Sopenharmony_ci
249ced56a00Sopenharmony_cisigned_char_test()
250ced56a00Sopenharmony_ci{
251ced56a00Sopenharmony_ci	log "Build and test using -fsigned-char"
252ced56a00Sopenharmony_ci	$MAKE CFLAGS="-O2 -Werror -fsigned-char" check
253ced56a00Sopenharmony_ci}
254ced56a00Sopenharmony_ciTEST_FUNCS+=(signed_char_test)
255ced56a00Sopenharmony_ci
256ced56a00Sopenharmony_ciwindows_build_test()
257ced56a00Sopenharmony_ci{
258ced56a00Sopenharmony_ci	log "Cross-compile for Windows (32-bit)"
259ced56a00Sopenharmony_ci	$MAKE CC=i686-w64-mingw32-gcc CFLAGS="-O2 -Werror"
260ced56a00Sopenharmony_ci
261ced56a00Sopenharmony_ci	log "Cross-compile for Windows (64-bit)"
262ced56a00Sopenharmony_ci	$MAKE CC=x86_64-w64-mingw32-gcc CFLAGS="-O2 -Werror"
263ced56a00Sopenharmony_ci}
264ced56a00Sopenharmony_ciTEST_FUNCS+=(windows_build_test)
265ced56a00Sopenharmony_ci
266ced56a00Sopenharmony_cisparse_test()
267ced56a00Sopenharmony_ci{
268ced56a00Sopenharmony_ci	log "Run sparse"
269ced56a00Sopenharmony_ci	./scripts/run-sparse.sh
270ced56a00Sopenharmony_ci}
271ced56a00Sopenharmony_ciTEST_FUNCS+=(sparse_test)
272ced56a00Sopenharmony_ci
273ced56a00Sopenharmony_ciclang_analyzer_test()
274ced56a00Sopenharmony_ci{
275ced56a00Sopenharmony_ci	log "Run clang static analyzer"
276ced56a00Sopenharmony_ci	scan-build --status-bugs make CFLAGS="-O2 -Werror" all test_programs
277ced56a00Sopenharmony_ci}
278ced56a00Sopenharmony_ciTEST_FUNCS+=(clang_analyzer_test)
279ced56a00Sopenharmony_ci
280ced56a00Sopenharmony_cishellcheck_test()
281ced56a00Sopenharmony_ci{
282ced56a00Sopenharmony_ci	log "Run shellcheck"
283ced56a00Sopenharmony_ci	shellcheck scripts/*.sh 1>&2
284ced56a00Sopenharmony_ci}
285ced56a00Sopenharmony_ciTEST_FUNCS+=(shellcheck_test)
286ced56a00Sopenharmony_ci
287ced56a00Sopenharmony_citest_exists()
288ced56a00Sopenharmony_ci{
289ced56a00Sopenharmony_ci	local tst=$1
290ced56a00Sopenharmony_ci	local func
291ced56a00Sopenharmony_ci	for func in "${TEST_FUNCS[@]}"; do
292ced56a00Sopenharmony_ci		if [ "${tst}_test" = "$func" ]; then
293ced56a00Sopenharmony_ci			return 0
294ced56a00Sopenharmony_ci		fi
295ced56a00Sopenharmony_ci	done
296ced56a00Sopenharmony_ci	return 1
297ced56a00Sopenharmony_ci}
298ced56a00Sopenharmony_ci
299ced56a00Sopenharmony_ciif [[ $# == 0 ]]; then
300ced56a00Sopenharmony_ci	for func in "${TEST_FUNCS[@]}"; do
301ced56a00Sopenharmony_ci		eval "$func"
302ced56a00Sopenharmony_ci	done
303ced56a00Sopenharmony_cielse
304ced56a00Sopenharmony_ci	for tst; do
305ced56a00Sopenharmony_ci		if ! test_exists "$tst"; then
306ced56a00Sopenharmony_ci			echo 1>&2 "Unknown test: $tst"
307ced56a00Sopenharmony_ci			exit 2
308ced56a00Sopenharmony_ci		fi
309ced56a00Sopenharmony_ci	done
310ced56a00Sopenharmony_ci	for tst; do
311ced56a00Sopenharmony_ci		eval "${tst}_test"
312ced56a00Sopenharmony_ci	done
313ced56a00Sopenharmony_cifi
314ced56a00Sopenharmony_ci
315ced56a00Sopenharmony_cilog "All tests passed!"
316