Merge pull request #7982 from gilles-peskine-arm/sample_program_demo_scripts-3.4

Run sample program demo scripts in all.sh
This commit is contained in:
Gilles Peskine 2023-10-18 10:03:52 +00:00 committed by GitHub
commit 0ea1b8fe8c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 237 additions and 74 deletions

137
programs/demo_common.sh Normal file
View file

@ -0,0 +1,137 @@
## Common shell functions used by demo scripts programs/*/*.sh.
## How to write a demo script
## ==========================
##
## Include this file near the top of each demo script:
## . "${0%/*}/../demo_common.sh"
##
## Start with a "msg" call that explains the purpose of the script.
## Then call the "depends_on" function to ensure that all config
## dependencies are met.
##
## As the last thing in the script, call the cleanup function.
##
## You can use the functions and variables described below.
set -e -u
## $root_dir is the root directory of the Mbed TLS source tree.
root_dir="${0%/*}"
# Find a nice path to the root directory, avoiding unnecessary "../".
# The code supports demo scripts nested up to 4 levels deep.
# The code works no matter where the demo script is relative to the current
# directory, even if it is called with a relative path.
n=4 # limit the search depth
while ! [ -d "$root_dir/programs" ] || ! [ -d "$root_dir/library" ]; do
if [ $n -eq 0 ]; then
echo >&2 "This doesn't seem to be an Mbed TLS source tree."
exit 125
fi
n=$((n - 1))
case $root_dir in
.) root_dir="..";;
..|?*/..) root_dir="$root_dir/..";;
?*/*) root_dir="${root_dir%/*}";;
/*) root_dir="/";;
*) root_dir=".";;
esac
done
## $programs_dir is the directory containing the sample programs.
# Assume an in-tree build.
programs_dir="$root_dir/programs"
## msg LINE...
## msg <TEXT_ORIGIN
## Display an informational message.
msg () {
if [ $# -eq 0 ]; then
sed 's/^/# /'
else
for x in "$@"; do
echo "# $x"
done
fi
}
## run "Message" COMMAND ARGUMENT...
## Display the message, then run COMMAND with the specified arguments.
run () {
echo
echo "# $1"
shift
echo "+ $*"
"$@"
}
## Like '!', but stop on failure with 'set -e'
not () {
if "$@"; then false; fi
}
## run_bad "Message" COMMAND ARGUMENT...
## Like run, but the command is expected to fail.
run_bad () {
echo
echo "$1 This must fail."
shift
echo "+ ! $*"
not "$@"
}
## config_has SYMBOL...
## Succeeds if the library configuration has all SYMBOLs set.
config_has () {
for x in "$@"; do
"$programs_dir/test/query_compile_time_config" "$x"
done
}
## depends_on SYMBOL...
## Exit if the library configuration does not have all SYMBOLs set.
depends_on () {
m=
for x in "$@"; do
if ! config_has "$x"; then
m="$m $x"
fi
done
if [ -n "$m" ]; then
cat >&2 <<EOF
$0: this demo requires the following
configuration options to be enabled at compile time:
$m
EOF
# Exit with a success status so that this counts as a pass for run_demos.py.
exit
fi
}
## Add the names of files to clean up to this whitespace-separated variable.
## The file names must not contain whitespace characters.
files_to_clean=
## Call this function at the end of each script.
## It is called automatically if the script is killed by a signal.
cleanup () {
rm -f -- $files_to_clean
}
################################################################
## End of the public interfaces. Code beyond this point is not
## meant to be called directly from a demo script.
trap 'cleanup; trap - HUP; kill -HUP $$' HUP
trap 'cleanup; trap - INT; kill -INT $$' INT
trap 'cleanup; trap - TERM; kill -TERM $$' TERM
if config_has MBEDTLS_ENTROPY_NV_SEED; then
# Create a seedfile that's sufficiently long in all library configurations.
# This is necessary for programs that use randomness.
# Assume that the name of the seedfile is the default name.
files_to_clean="$files_to_clean seedfile"
dd if=/dev/urandom of=seedfile ibs=64 obs=64 count=1
fi

View file

@ -15,36 +15,17 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
set -e -u . "${0%/*}/../demo_common.sh"
program_name="key_ladder_demo" msg <<'EOF'
program="${0%/*}/$program_name" This script demonstrates the use of the PSA cryptography interface to
files_to_clean= create a master key, derive a key from it and use that derived key to
wrap some data using an AEAD algorithm.
EOF
if [ ! -e "$program" ]; then depends_on MBEDTLS_SHA256_C MBEDTLS_MD_C MBEDTLS_AES_C MBEDTLS_CCM_C MBEDTLS_PSA_CRYPTO_C MBEDTLS_FS_IO
# Look for programs in the current directory and the directories above it
for dir in "." ".." "../.."; do
program="$dir/programs/psa/$program_name"
if [ -e "$program" ]; then
break
fi
done
if [ ! -e "$program" ]; then
echo "Could not find $program_name executable"
echo "If building out-of-tree, this script must be run" \ program="${0%/*}"/key_ladder_demo
"from the project build directory."
exit 1
fi
fi
run () {
echo
echo "# $1"
shift
echo "+ $*"
"$@"
}
if [ -e master.key ]; then if [ -e master.key ]; then
echo "# Reusing the existing master.key file." echo "# Reusing the existing master.key file."
@ -68,7 +49,7 @@ run "Compare the unwrapped data with the original input." \
cmp input.txt hello_world.txt cmp input.txt hello_world.txt
files_to_clean="$files_to_clean hellow_orld.txt" files_to_clean="$files_to_clean hellow_orld.txt"
! run "Derive a different key and attempt to unwrap the data. This must fail." \ run_bad "Derive a different key and attempt to unwrap the data." \
"$program" unwrap master=master.key input=hello_world.wrap output=hellow_orld.txt label=hellow label=orld "$program" unwrap master=master.key input=hello_world.wrap output=hellow_orld.txt label=hellow label=orld
files_to_clean="$files_to_clean hello.key" files_to_clean="$files_to_clean hello.key"
@ -79,5 +60,4 @@ run "Check that we get the same key by unwrapping data made by the other key." \
"$program" unwrap master=hello.key label=world \ "$program" unwrap master=hello.key label=world \
input=hello_world.wrap output=hello_world.txt input=hello_world.wrap output=hello_world.txt
# Cleanup cleanup
rm -f $files_to_clean

View file

@ -18,34 +18,21 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
set -e -u . "${0%/*}/../demo_common.sh"
program_name="dlopen" msg "Test the dynamic loading of libmbed*"
program_dir="${0%/*}"
program="$program_dir/$program_name"
program="$programs_dir/test/dlopen"
library_dir="$root_dir/library"
# Skip this test if we don't have a shared library build. Detect this
# through the absence of the demo program.
if [ ! -e "$program" ]; then if [ ! -e "$program" ]; then
# Look for programs in the current directory and the directories above it msg "$0: this demo requires a shared library build."
for dir in "." ".." "../.."; do # Exit with a success status so that this counts as a pass for run_demos.py.
program_dir="$dir/programs/test" exit
program="$program_dir/$program_name"
if [ -e "$program" ]; then
break
fi
done
if [ ! -e "$program" ]; then
echo "Could not find $program_name program"
echo "Make sure that Mbed TLS is built as a shared library." \
"If building out-of-tree, this script must be run" \
"from the project build directory."
exit 1
fi
fi fi
top_dir="$program_dir/../.."
library_dir="$top_dir/library"
# ELF-based Unix-like (Linux, *BSD, Solaris, ...) # ELF-based Unix-like (Linux, *BSD, Solaris, ...)
if [ -n "${LD_LIBRARY_PATH-}" ]; then if [ -n "${LD_LIBRARY_PATH-}" ]; then
LD_LIBRARY_PATH="$library_dir:$LD_LIBRARY_PATH" LD_LIBRARY_PATH="$library_dir:$LD_LIBRARY_PATH"
@ -62,6 +49,6 @@ else
fi fi
export DYLD_LIBRARY_PATH export DYLD_LIBRARY_PATH
echo "Running dynamic loading test program: $program" msg "Running dynamic loading test program: $program"
echo "Loading libraries from: $library_dir" msg "Loading libraries from: $library_dir"
"$program" "$program"

View file

@ -1063,6 +1063,9 @@ component_test_default_out_of_box () {
msg "selftest: make, default config (out-of-box)" # ~10s msg "selftest: make, default config (out-of-box)" # ~10s
programs/test/selftest programs/test/selftest
msg "program demos: make, default config (out-of-box)" # ~10s
tests/scripts/run_demos.py
} }
component_test_default_cmake_gcc_asan () { component_test_default_cmake_gcc_asan () {
@ -1073,6 +1076,9 @@ component_test_default_cmake_gcc_asan () {
msg "test: main suites (inc. selftests) (ASan build)" # ~ 50s msg "test: main suites (inc. selftests) (ASan build)" # ~ 50s
make test make test
msg "program demos (ASan build)" # ~10s
tests/scripts/run_demos.py
msg "test: selftest (ASan build)" # ~ 10s msg "test: selftest (ASan build)" # ~ 10s
programs/test/selftest programs/test/selftest
@ -1862,6 +1868,9 @@ component_test_full_cmake_clang () {
msg "test: cpp_dummy_build (full config, clang)" # ~ 1s msg "test: cpp_dummy_build (full config, clang)" # ~ 1s
programs/test/cpp_dummy_build programs/test/cpp_dummy_build
msg "program demos (full config, clang)" # ~10s
tests/scripts/run_demos.py
msg "test: psa_constant_names (full config, clang)" # ~ 1s msg "test: psa_constant_names (full config, clang)" # ~ 1s
tests/scripts/test_psa_constant_names.py tests/scripts/test_psa_constant_names.py
@ -2045,6 +2054,9 @@ component_test_full_deprecated_warning () {
msg "test: full config + MBEDTLS_TEST_DEPRECATED" # ~ 30s msg "test: full config + MBEDTLS_TEST_DEPRECATED" # ~ 30s
make test make test
msg "program demos: full config + MBEDTLS_TEST_DEPRECATED" # ~10s
tests/scripts/run_demos.py
} }
# Check that the specified libraries exist and are empty. # Check that the specified libraries exist and are empty.
@ -5192,6 +5204,9 @@ component_test_memsan () {
msg "test: main suites (MSan)" # ~ 10s msg "test: main suites (MSan)" # ~ 10s
make test make test
msg "program demos (MSan)" # ~20s
tests/scripts/run_demos.py
msg "test: ssl-opt.sh (MSan)" # ~ 1 min msg "test: ssl-opt.sh (MSan)" # ~ 1 min
tests/ssl-opt.sh tests/ssl-opt.sh

View file

@ -162,24 +162,6 @@ def is_windows_file(filepath):
return ext in ('.bat', '.dsp', '.dsw', '.sln', '.vcxproj') return ext in ('.bat', '.dsp', '.dsw', '.sln', '.vcxproj')
class PermissionIssueTracker(FileIssueTracker):
"""Track files with bad permissions.
Files that are not executable scripts must not be executable."""
heading = "Incorrect permissions:"
# .py files can be either full scripts or modules, so they may or may
# not be executable.
suffix_exemptions = frozenset({".py"})
def check_file_for_issue(self, filepath):
is_executable = os.access(filepath, os.X_OK)
should_be_executable = filepath.endswith((".sh", ".pl"))
if is_executable != should_be_executable:
self.files_with_issues[filepath] = None
class ShebangIssueTracker(FileIssueTracker): class ShebangIssueTracker(FileIssueTracker):
"""Track files with a bad, missing or extraneous shebang line. """Track files with a bad, missing or extraneous shebang line.
@ -386,7 +368,6 @@ class IntegrityChecker:
self.logger = None self.logger = None
self.setup_logger(log_file) self.setup_logger(log_file)
self.issues_to_check = [ self.issues_to_check = [
PermissionIssueTracker(),
ShebangIssueTracker(), ShebangIssueTracker(),
EndOfFileNewlineIssueTracker(), EndOfFileNewlineIssueTracker(),
Utf8BomIssueTracker(), Utf8BomIssueTracker(),

63
tests/scripts/run_demos.py Executable file
View file

@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""Run the Mbed TLS demo scripts.
"""
import argparse
import glob
import subprocess
import sys
def run_demo(demo, quiet=False):
"""Run the specified demo script. Return True if it succeeds."""
args = {}
if quiet:
args['stdout'] = subprocess.DEVNULL
args['stderr'] = subprocess.DEVNULL
returncode = subprocess.call([demo], **args)
return returncode == 0
def run_demos(demos, quiet=False):
"""Run the specified demos and print summary information about failures.
Return True if all demos passed and False if a demo fails.
"""
failures = []
for demo in demos:
if not quiet:
print('#### {} ####'.format(demo))
success = run_demo(demo, quiet=quiet)
if not success:
failures.append(demo)
if not quiet:
print('{}: FAIL'.format(demo))
if quiet:
print('{}: {}'.format(demo, 'PASS' if success else 'FAIL'))
else:
print('')
successes = len(demos) - len(failures)
print('{}/{} demos passed'.format(successes, len(demos)))
if failures and not quiet:
print('Failures:', *failures)
return not failures
def run_all_demos(quiet=False):
"""Run all the available demos.
Return True if all demos passed and False if a demo fails.
"""
all_demos = glob.glob('programs/*/*_demo.sh')
if not all_demos:
# Keep the message on one line. pylint: disable=line-too-long
raise Exception('No demos found. run_demos needs to operate from the Mbed TLS toplevel directory.')
return run_demos(all_demos, quiet=quiet)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--quiet', '-q',
action='store_true',
help="suppress the output of demos")
options = parser.parse_args()
success = run_all_demos(quiet=options.quiet)
sys.exit(0 if success else 1)
if __name__ == '__main__':
main()