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