diff --git a/Jenkinsfile b/Jenkinsfile index 5c5e9e483..cb5d141a7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -114,6 +114,36 @@ pipeline{ } } } + stage ('Worker build SGX') { + agent { node { label 'bionic-sgx' } } + options { + timeout(time: 1, unit: 'HOURS') + } + when { branch 'master' } + stages { + stage ('Checkout') { + steps { + checkout scm + } + } + stage ('Run SGX integration tests') { + steps { + sh "scripts/dev_cli.sh tests --integration-sgx" + } + } + stage ('Run SGX integration tests for musl') { + steps { + sh "scripts/dev_cli.sh tests --integration-sgx --libc musl" + } + } + } + post { + always { + sh "sudo chown -R jenkins.jenkins ${WORKSPACE}" + deleteDir() + } + } + } } } } diff --git a/scripts/dev_cli.sh b/scripts/dev_cli.sh index 41b1e2be2..36d481786 100755 --- a/scripts/dev_cli.sh +++ b/scripts/dev_cli.sh @@ -161,6 +161,7 @@ cmd_help() { echo " --unit Run the unit tests." echo " --cargo Run the cargo tests." echo " --integration Run the integration tests." + echo " --integration-sgx Run the SGX integration tests." echo " --libc Select the C library Cloud Hypervisor will be built against. Default is gnu" echo " --all Run all tests." echo "" @@ -246,6 +247,7 @@ cmd_tests() { unit=false cargo=false integration=false + integration_sgx=false libc="gnu" while [ $# -gt 0 ]; do @@ -254,6 +256,7 @@ cmd_tests() { "--unit") { unit=true; } ;; "--cargo") { cargo=true; } ;; "--integration") { integration=true; } ;; + "--integration-sgx") { integration_sgx=true; } ;; "--libc") shift [[ "$1" =~ ^(musl|gnu)$ ]] || \ @@ -322,6 +325,25 @@ cmd_tests() { ./scripts/run_integration_tests_$(uname -m).sh "$@" || fix_dir_perms $? || exit $? fi + if [ "$integration_sgx" = true ] ; then + say "Running integration tests for $target..." + $DOCKER_RUNTIME run \ + --workdir "$CTR_CLH_ROOT_DIR" \ + --rm \ + --privileged \ + --security-opt seccomp=unconfined \ + --ipc=host \ + --net="$CTR_CLH_NET" \ + --mount type=tmpfs,destination=/tmp \ + --volume /dev:/dev \ + --volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" \ + --volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \ + --env USER="root" \ + --env CH_LIBC="${libc}" \ + "$CTR_IMAGE" \ + ./scripts/run_integration_tests_sgx.sh "$@" || fix_dir_perms $? || exit $? + fi + fix_dir_perms $? } diff --git a/scripts/run_integration_tests_sgx.sh b/scripts/run_integration_tests_sgx.sh new file mode 100755 index 000000000..69a6a8ee8 --- /dev/null +++ b/scripts/run_integration_tests_sgx.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -x + +source $HOME/.cargo/env + +BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}" +CFLAGS="" +TARGET_CC="" +if [[ "${BUILD_TARGET}" == "x86_64-unknown-linux-musl" ]]; then +TARGET_CC="musl-gcc" +CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/" +fi + +cargo build --all --release --target $BUILD_TARGET +strip target/$BUILD_TARGET/release/cloud-hypervisor + +export RUST_BACKTRACE=1 + +time cargo test --features "integration_tests" "tests::sgx::$@" +RES=$? + +if [ $RES -eq 0 ]; then + # virtio-mmio based testing + cargo build --all --release --target $BUILD_TARGET --no-default-features --features "mmio,kvm" + strip target/$BUILD_TARGET/release/cloud-hypervisor + + time cargo test --features "integration_tests,mmio" "tests::sgx::$@" + RES=$? +fi + +exit $RES diff --git a/tests/integration.rs b/tests/integration.rs index f3ea8e8b1..74fd1dc67 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -84,6 +84,8 @@ mod tests { const BIONIC_IMAGE_NAME: &str = "bionic-server-cloudimg-amd64"; #[cfg(target_arch = "x86_64")] const FOCAL_IMAGE_NAME: &str = "focal-server-cloudimg-amd64-custom"; + #[cfg(target_arch = "x86_64")] + const FOCAL_SGX_IMAGE_NAME: &str = "focal-server-cloudimg-amd64-sgx"; #[cfg(target_arch = "aarch64")] const BIONIC_IMAGE_NAME: &str = "bionic-server-cloudimg-arm64"; #[cfg(target_arch = "aarch64")] @@ -776,6 +778,41 @@ mod tests { } } + fn check_sgx_support(&self) -> Result { + if self + .ssh_command( + "cpuid -l 0x7 -s 0 | tr -s [:space:] | grep -q 'SGX: \ + Software Guard Extensions supported = true' && echo ok", + )? + .trim() + != "ok" + { + return Ok(false); + } + if self + .ssh_command( + "cpuid -l 0x7 -s 0 | tr -s [:space:] | grep -q 'SGX_LC: \ + SGX launch config supported = true' && echo ok", + )? + .trim() + != "ok" + { + return Ok(false); + } + if self + .ssh_command( + "cpuid -l 0x12 -s 0 | tr -s [:space:] | grep -q 'SGX1 \ + supported = true' && echo ok", + )? + .trim() + != "ok" + { + return Ok(false); + } + + Ok(true) + } + fn get_entropy(&self) -> Result { Ok(self .ssh_command("cat /proc/sys/kernel/random/entropy_avail")? @@ -5100,4 +5137,65 @@ mod tests { test_memory_mergeable(true) } } + + mod sgx { + use crate::tests::*; + + #[test] + #[cfg(target_arch = "x86_64")] + fn test_sgx() { + let mut focal = UbuntuDiskConfig::new(FOCAL_SGX_IMAGE_NAME.to_string()); + let guest = Guest::new(&mut focal); + let mut workload_path = dirs::home_dir().unwrap(); + workload_path.push("workloads"); + + let mut kernel_path = workload_path; + kernel_path.push("bzImage_w_sgx"); + + let mut child = GuestCommand::new(&guest) + .args(&["--cpus", "boot=1"]) + .args(&["--memory", "size=512M"]) + .args(&["--kernel", kernel_path.to_str().unwrap()]) + .default_disks() + .default_net() + .args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE]) + .args(&["--sgx-epc", "size=64M"]) + .capture_output() + .spawn() + .unwrap(); + + thread::sleep(std::time::Duration::new(20, 0)); + + let r = std::panic::catch_unwind(|| { + // Check if SGX is correctly detected in the guest. + assert!(guest.check_sgx_support().unwrap()); + + // Validate the SGX EPC section is 64MiB. + assert_eq!( + guest + .ssh_command("cpuid -l 0x12 -s 2 | grep 'section size' | cut -d '=' -f 2") + .unwrap_or_default() + .trim(), + "0x0000000004000000" + ); + + // Run a test relying on SGX enclaves and check if it runs + // successfully. + assert!(guest + .ssh_command("cd /linux-sgx/SampleCode/LocalAttestation/bin/ && sudo ./app") + .unwrap_or_default() + .trim() + .contains( + "succeed to load enclaves.\nsucceed to \ + establish secure channel.\nSucceed to exchange \ + secure message...\nSucceed to close Session..." + )); + }); + + let _ = child.kill(); + let output = child.wait_with_output().unwrap(); + + handle_child_output(r, &output); + } + } }