From 1c26d11b96cb709d945aea860f846fc30b080afa Mon Sep 17 00:00:00 2001 From: maddeleine <59030281+maddeleine@users.noreply.github.com> Date: Fri, 2 Jun 2023 15:09:33 -0700 Subject: [PATCH] Fixes dynamic loading bug (#4024) --- codebuild/bin/s2n_codebuild.sh | 1 + codebuild/bin/s2n_dynamic_load_test.c | 79 +++++++++++++++++++++++++++ codebuild/bin/test_dynamic_load.sh | 56 +++++++++++++++++++ nix/README.md | 6 +- nix/shell.sh | 5 ++ utils/s2n_random.c | 3 + 6 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 codebuild/bin/s2n_dynamic_load_test.c create mode 100755 codebuild/bin/test_dynamic_load.sh diff --git a/codebuild/bin/s2n_codebuild.sh b/codebuild/bin/s2n_codebuild.sh index dd3eecfe0f5..644766e4607 100755 --- a/codebuild/bin/s2n_codebuild.sh +++ b/codebuild/bin/s2n_codebuild.sh @@ -134,6 +134,7 @@ if [[ "$TESTS" == "ALL" || "$TESTS" == "asan" ]]; then make clean; S2N_ADDRESS_S if [[ "$TESTS" == "ALL" || "$TESTS" == "integrationv2" ]]; then run_integration_v2_tests; fi if [[ "$TESTS" == "ALL" || "$TESTS" == "crt" ]]; then ./codebuild/bin/build_aws_crt_cpp.sh $(mktemp -d) $(mktemp -d); fi if [[ "$TESTS" == "ALL" || "$TESTS" == "sharedandstatic" ]]; then ./codebuild/bin/test_install_shared_and_static.sh $(mktemp -d); fi +if [[ "$TESTS" == "ALL" || "$TESTS" == "dynamicload" ]]; then ./codebuild/bin/test_dynamic_load.sh $(mktemp -d); fi if [[ "$TESTS" == "ALL" || "$TESTS" == "fuzz" ]]; then (make clean && make fuzz) ; fi if [[ "$TESTS" == "ALL" || "$TESTS" == "benchmark" ]]; then (make clean && make benchmark) ; fi if [[ "$TESTS" == "sawHMAC" ]] && [[ "$OS_NAME" == "linux" ]]; then make -C tests/saw/ tmp/verify_HMAC.log ; fi diff --git a/codebuild/bin/s2n_dynamic_load_test.c b/codebuild/bin/s2n_dynamic_load_test.c new file mode 100644 index 00000000000..3cac2d33c54 --- /dev/null +++ b/codebuild/bin/s2n_dynamic_load_test.c @@ -0,0 +1,79 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include +#include +#include +#include + +static void *s2n_load_dynamic_lib(void *ctx) +{ + const char *s2n_so_path = ctx; + + void *s2n_so = dlopen(s2n_so_path, RTLD_NOW); + if (!s2n_so) { + exit(1); + } + + int (*s2n_init_dl)(void) = NULL; + *(void **) (&s2n_init_dl) = dlsym(s2n_so, "s2n_init"); + if (dlerror()) { + exit(1); + } + + int (*s2n_cleanup_dl)(void) = NULL; + *(void **) (&s2n_cleanup_dl) = dlsym(s2n_so, "s2n_cleanup"); + if (dlerror()) { + exit(1); + } + + if ((*s2n_init_dl)()) { + exit(1); + } + if ((*s2n_cleanup_dl)()) { + exit(1); + } + + if (dlclose(s2n_so)) { + exit(1); + } + + return NULL; +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + printf("Usage: s2n_dynamic_load_test \n"); + exit(1); + } + + /* s2n-tls library can be dynamically loaded and cleaned up safely + * + * We can't use any s2n test macros because this test doesn't get linked to + * s2n during compile-time. + */ + { + pthread_t thread_id = { 0 }; + if (pthread_create(&thread_id, NULL, &s2n_load_dynamic_lib, argv[1])) { + exit(1); + } + if (pthread_join(thread_id, NULL)) { + exit(1); + } + } + + return 0; +} diff --git a/codebuild/bin/test_dynamic_load.sh b/codebuild/bin/test_dynamic_load.sh new file mode 100755 index 00000000000..e75554ab668 --- /dev/null +++ b/codebuild/bin/test_dynamic_load.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. +# + +# This script compiles s2n-tls as a shared library and compiles a test +# without linking to the library. This enables us to test behavior when +# s2n-tls is dynamically loaded. + +WORK_DIR=$1 + +if [ ! -z "$NIX_STORE" ]; then + OPENSSL=$(which openssl) + LIBCRYPTO_ROOT=$(nix-store --query $OPENSSL) +else + source codebuild/bin/s2n_setup_env.sh +fi + +S2N_BUILD_ARGS=(-H. -DCMAKE_PREFIX_PATH=$LIBCRYPTO_ROOT -DBUILD_TESTING=OFF) + +# create installation dir with libs2n.so +if [ ! -d $WORK_DIR/s2n-install-shared ]; then + (set -x; cmake -B$WORK_DIR/s2n-build-shared -DCMAKE_INSTALL_PREFIX=$WORK_DIR/s2n-install-shared -DBUILD_SHARED_LIBS=ON ${S2N_BUILD_ARGS[@]}) + (set -x; cmake --build $WORK_DIR/s2n-build-shared --target install -- -j $(nproc)) +fi + +# Compile the test file +$CC -Wl,-rpath $LIBCRYPTO_ROOT -o s2n_dynamic_load_test codebuild/bin/s2n_dynamic_load_test.c -ldl -lpthread + +LDD_OUTPUT=$(ldd s2n_dynamic_load_test) + +# Confirm executable doesn't have libs2n.so loaded +if echo "$LDD_OUTPUT" | grep -q libs2n; then + echo "test failure: libs2n should not appear in ldd output" + exit 1 +fi + +# Run the test with the path to libs2n +echo "Running s2n_dynamic_load_test" +LD_LIBRARY_PATH=$LIBCRYPTO_ROOT/lib ./s2n_dynamic_load_test $WORK_DIR/s2n-install-shared/lib/libs2n.so +returncode=$? +if [ $returncode -ne 0 ]; then + echo "test failure: s2n_dynamic_load_test did not succeed" + exit 1 +fi +echo "Passed s2n_dynamic_load_test" diff --git a/nix/README.md b/nix/README.md index 4c0dd7b64f5..8ae5e220637 100644 --- a/nix/README.md +++ b/nix/README.md @@ -10,7 +10,7 @@ In the context of s2n-tls, we're using it to ease the setup of development envir - `sudo bash -c “mkdir /nix && chmod 755 /nix && chown -R $USERNAME /nix”` - Run the single-user command from `https://nixos.org/download.html#nix-install-linux` -- Enable flakes: `mkdir ~/.config/nix; echo "experimental-features = nix-command flakes" > ~/.config/nix.conf` +- Enable flakes: `mkdir ~/.config/nix; echo "experimental-features = nix-command flakes" > ~/.config/nix/nix.conf` - `cd s2n-tls` #### What is this doing? @@ -39,7 +39,7 @@ The first time this is run, it might take a while to build everything. From inside the devShell after configuring and build finish, run `unit `, or with no test name for all of the tests. For example, to run the stuffer_test use: `unit stuffer_test`, or `unit stuffer` to run all of tests with stuffer in the name. -The CI does this in one shot with: `nix develop --max-jobs auto --ignore-environnment --command bash -c "source ./nix/shell.sh; configure;build;unit" `. +The CI does this in one shot with: `nix develop --max-jobs auto --ignore-environment --command bash -c "source ./nix/shell.sh; configure;build;unit" `. What is this doing? @@ -66,7 +66,7 @@ In its simplest form, the `nix copy` command can be used to stash a specific pac By using inputDerivation, we can create a meta-package that contains all the packages in our devShell. -As an exmample, this copy will stash the s2n-tls devShell: +As an example, this copy will stash the s2n-tls devShell: ``` nix copy --to 's3://my-nix-chache-bucket?region=us-west-2' .#devShell diff --git a/nix/shell.sh b/nix/shell.sh index 035f48d1d09..efede6efb00 100644 --- a/nix/shell.sh +++ b/nix/shell.sh @@ -131,3 +131,8 @@ function test_toolchain_counts { echo -e "python nassl:\t $(pip freeze|grep -c 'nassl')" } +function test_nonstandard_compilation { + # Any script that needs to compile s2n in a non-standard way can run here + ./codebuild/bin/test_dynamic_load.sh $(mktemp -d) +} + diff --git a/utils/s2n_random.c b/utils/s2n_random.c index f58514f249e..98ba8098610 100644 --- a/utils/s2n_random.c +++ b/utils/s2n_random.c @@ -530,6 +530,9 @@ S2N_RESULT s2n_rand_cleanup_thread(void) s2n_per_thread_rand_state.drbgs_initialized = false; + /* Unset the thread-local destructor */ + pthread_setspecific(s2n_per_thread_rand_state_key, NULL); + return S2N_RESULT_OK; }