Skip to content

Commit

Permalink
Merge pull request #7983 from gilles-peskine-arm/sample-program-demos…
Browse files Browse the repository at this point in the history
…-2.28

Backport 2.28: Run sample program demo scripts in all.sh
  • Loading branch information
gilles-peskine-arm authored Oct 19, 2023
2 parents 0ee9dac + ba986f3 commit 7e36ad1
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 74 deletions.
137 changes: 137 additions & 0 deletions programs/demo_common.sh
Original file line number Diff line number Diff line change
@@ -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
40 changes: 10 additions & 30 deletions programs/psa/key_ladder_demo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.

set -e -u
. "${0%/*}/../demo_common.sh"

program_name="key_ladder_demo"
program="${0%/*}/$program_name"
files_to_clean=
msg <<'EOF'
This script demonstrates the use of the PSA cryptography interface to
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
# 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"
depends_on MBEDTLS_SHA256_C MBEDTLS_MD_C MBEDTLS_AES_C MBEDTLS_CCM_C MBEDTLS_PSA_CRYPTO_C MBEDTLS_FS_IO

echo "If building out-of-tree, this script must be run" \
"from the project build directory."
exit 1
fi
fi

run () {
echo
echo "# $1"
shift
echo "+ $*"
"$@"
}
program="${0%/*}"/key_ladder_demo

if [ -e master.key ]; then
echo "# Reusing the existing master.key file."
Expand All @@ -68,7 +49,7 @@ run "Compare the unwrapped data with the original input." \
cmp input.txt hello_world.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

files_to_clean="$files_to_clean hello.key"
Expand All @@ -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 \
input=hello_world.wrap output=hello_world.txt

# Cleanup
rm -f $files_to_clean
cleanup
37 changes: 12 additions & 25 deletions programs/test/dlopen_demo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.

set -e -u
. "${0%/*}/../demo_common.sh"

program_name="dlopen"
program_dir="${0%/*}"
program="$program_dir/$program_name"
msg "Test the dynamic loading of libmbed*"

if [ ! -e "$program" ]; then
# Look for programs in the current directory and the directories above it
for dir in "." ".." "../.."; do
program_dir="$dir/programs/test"
program="$program_dir/$program_name"
if [ -e "$program" ]; then
break
fi
done
if [ ! -e "$program" ]; then
echo "Could not find $program_name program"
program="$programs_dir/test/dlopen"
library_dir="$root_dir/library"

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
# 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
msg "$0: this demo requires a shared library build."
# Exit with a success status so that this counts as a pass for run_demos.py.
exit
fi

top_dir="$program_dir/../.."
library_dir="$top_dir/library"

# ELF-based Unix-like (Linux, *BSD, Solaris, ...)
if [ -n "${LD_LIBRARY_PATH-}" ]; then
LD_LIBRARY_PATH="$library_dir:$LD_LIBRARY_PATH"
Expand All @@ -62,6 +49,6 @@ else
fi
export DYLD_LIBRARY_PATH

echo "Running dynamic loading test program: $program"
echo "Loading libraries from: $library_dir"
msg "Running dynamic loading test program: $program"
msg "Loading libraries from: $library_dir"
"$program"
15 changes: 15 additions & 0 deletions tests/scripts/all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,9 @@ component_test_default_out_of_box () {
msg "selftest: make, default config (out-of-box)" # ~10s
programs/test/selftest
msg "program demos: make, default config (out-of-box)" # ~10s
tests/scripts/run_demos.py
}
component_test_default_cmake_gcc_asan () {
Expand All @@ -878,6 +881,9 @@ component_test_default_cmake_gcc_asan () {
msg "test: main suites (inc. selftests) (ASan build)" # ~ 50s
make test
msg "program demos (ASan build)" # ~10s
tests/scripts/run_demos.py
msg "test: selftest (ASan build)" # ~ 10s
programs/test/selftest
Expand Down Expand Up @@ -1578,6 +1584,9 @@ component_test_full_cmake_clang () {
msg "test: cpp_dummy_build (full config, clang)" # ~ 1s
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
tests/scripts/test_psa_constant_names.py
Expand Down Expand Up @@ -1698,6 +1707,9 @@ component_test_full_deprecated_warning () {
msg "test: full config + MBEDTLS_TEST_DEPRECATED" # ~ 30s
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.
Expand Down Expand Up @@ -3344,6 +3356,9 @@ component_test_memsan () {
msg "test: main suites (MSan)" # ~ 10s
make test
msg "program demos (MSan)" # ~20s
tests/scripts/run_demos.py
msg "test: ssl-opt.sh (MSan)" # ~ 1 min
tests/ssl-opt.sh
Expand Down
19 changes: 0 additions & 19 deletions tests/scripts/check_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,24 +162,6 @@ def is_windows_file(filepath):
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):
"""Track files with a bad, missing or extraneous shebang line.
Expand Down Expand Up @@ -386,7 +368,6 @@ def __init__(self, log_file):
self.logger = None
self.setup_logger(log_file)
self.issues_to_check = [
PermissionIssueTracker(),
ShebangIssueTracker(),
EndOfFileNewlineIssueTracker(),
Utf8BomIssueTracker(),
Expand Down
63 changes: 63 additions & 0 deletions tests/scripts/run_demos.py
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit 7e36ad1

Please sign in to comment.