From 524941ecb5bc785b51221868e2f112417a0493fb Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Fri, 29 Aug 2025 22:49:26 +0000 Subject: [PATCH 1/9] Add target triple support to justfile Signed-off-by: James Sturtevant --- Cross.toml | 9 ++ Justfile | 106 ++++++++++++---------- hack/clippy-package-features.sh | 28 ++++-- src/hyperlight_host/src/mem/shared_mem.rs | 11 ++- 4 files changed, 96 insertions(+), 58 deletions(-) create mode 100644 Cross.toml diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 000000000..7fa77eac0 --- /dev/null +++ b/Cross.toml @@ -0,0 +1,9 @@ +[build] +pre-build = [ + "apt-get update && apt-get -y install gdb" +] + +[build.env] +passthrough = [ + "TARGET_TRIPLE", # Some tests invoke Cargo directly and need this to run correctly +] \ No newline at end of file diff --git a/Justfile b/Justfile index 6e8218958..e9b8b1bd6 100644 --- a/Justfile +++ b/Justfile @@ -6,6 +6,20 @@ set dotenv-load := true set-env-command := if os() == "windows" { "$env:" } else { "export " } bin-suffix := if os() == "windows" { ".bat" } else { ".sh" } +################ +### cross-rs ### +################ +target-triple := env('TARGET_TRIPLE', "") +docker := if target-triple != "" { require("docker") } else { "" } +# this command is only used host side not for guests +# include the --target-dir for the cross builds. This ensures that the builds are separated and avoid any conflicts with the guest builds +cargo-cmd := if target-triple != "" { require("cross") } else { "cargo" } +target-triple-flag := if target-triple != "" { "--target " + target-triple + " --target-dir ./target/host"} else { "" } +# set up cross to use the devices +kvm-gid := if path_exists("/dev/kvm") == "true" { `getent group kvm | cut -d: -f3` } else { "" } +export CROSS_CONTAINER_OPTS := if path_exists("/dev/kvm") == "true" { "--device=/dev/kvm" } else if path_exists("/dev/mshv") == "true" { "--device=/dev/mshv" } else { "" } +export CROSS_CONTAINER_GID := if path_exists("/dev/kvm") == "true" { kvm-gid } else {"1000"} # required to have ownership of the mapped in device on kvm + root := justfile_directory() default-target := "debug" @@ -23,12 +37,7 @@ alias cg := build-and-move-c-guests # build host library build target=default-target: - cargo build --profile={{ if target == "debug" { "dev" } else { target } }} - -# build host library -build-with-musl-libc target=default-target: - cargo build --profile={{ if target == "debug" { "dev" } else { target } }} --target x86_64-unknown-linux-musl - + {{ cargo-cmd }} build --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} # build testing guest binaries guests: build-and-move-rust-guests build-and-move-c-guests @@ -75,10 +84,7 @@ test-like-ci config=default-target hypervisor="kvm": just test {{config}} seccomp,build-metadata,init-paging,{{ if hypervisor == "mshv" {"mshv2"} else if hypervisor == "mshv3" {"mshv3"} else {"kvm"} }} @# make sure certain cargo features compile - cargo check -p hyperlight-host --features crashdump - cargo check -p hyperlight-host --features print_debug - cargo check -p hyperlight-host --features gdb - cargo check -p hyperlight-host --features trace_guest,unwind_guest,mem_profile + just check @# without any driver (should fail to compile) just test-compilation-no-default-features {{config}} @@ -143,64 +149,65 @@ test target=default-target features="": (test-unit target features) (test-isolat # runs unit tests test-unit target=default-target features="": - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} --lib + {{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --lib # runs tests that requires being run separately, for example due to global state -test-isolated target=default-target features="": - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- sandbox::uninitialized::tests::test_trace_trace --exact --ignored - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- sandbox::uninitialized::tests::test_log_trace --exact --ignored - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::create_1000_sandboxes --exact --ignored - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- sandbox::outb::tests::test_log_outb_log --exact --ignored - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- mem::shared_mem::tests::test_drop --exact --ignored - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --test integration_test -- log_message --exact --ignored +test-isolated target=default-target features="" : + {{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::uninitialized::tests::test_trace_trace --exact --ignored + {{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::uninitialized::tests::test_log_trace --exact --ignored + {{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::create_1000_sandboxes --exact --ignored + {{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::outb::tests::test_log_outb_log --exact --ignored + {{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- mem::shared_mem::tests::test_drop --exact --ignored + {{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --test integration_test -- log_message --exact --ignored @# metrics tests - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F function_call_metrics,init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- metrics::tests::test_metrics_are_emitted --exact + {{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F function_call_metrics,init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- metrics::tests::test_metrics_are_emitted --exact # runs integration tests. Guest can either be "rust" or "c" test-integration guest target=default-target features="": @# run execute_on_heap test with feature "executable_heap" on and off - {{if os() == "windows" { "$env:" } else { "" } }}GUEST="{{guest}}"{{if os() == "windows" { ";" } else { "" } }} cargo test --profile={{ if target == "debug" { "dev" } else { target } }} --test integration_test execute_on_heap {{ if features =="" {" --features executable_heap"} else {"--features executable_heap," + features} }} -- --ignored - {{if os() == "windows" { "$env:" } else { "" } }}GUEST="{{guest}}"{{if os() == "windows" { ";" } else { "" } }} cargo test --profile={{ if target == "debug" { "dev" } else { target } }} --test integration_test execute_on_heap {{ if features =="" {""} else {"--features " + features} }} -- --ignored + {{if os() == "windows" { "$env:" } else { "" } }}GUEST="{{guest}}"{{if os() == "windows" { ";" } else { "" } }} {{ cargo-cmd }} test --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --test integration_test execute_on_heap {{ if features =="" {" --features executable_heap"} else {"--features executable_heap," + features} }} -- --ignored + {{if os() == "windows" { "$env:" } else { "" } }}GUEST="{{guest}}"{{if os() == "windows" { ";" } else { "" } }} {{ cargo-cmd }} test --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --test integration_test execute_on_heap {{ if features =="" {""} else {"--features " + features} }} -- --ignored @# run the rest of the integration tests - {{if os() == "windows" { "$env:" } else { "" } }}GUEST="{{guest}}"{{if os() == "windows" { ";" } else { "" } }} cargo test -p hyperlight-host {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} --test '*' + {{if os() == "windows" { "$env:" } else { "" } }}GUEST="{{guest}}"{{if os() == "windows" { ";" } else { "" } }} {{ cargo-cmd }} test -p hyperlight-host {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F init-paging," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --test '*' # runs seccomp tests test-seccomp target=default-target features="": @# run seccomp test with feature "seccomp" on and off - cargo test --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host test_violate_seccomp_filters --lib {{ if features =="" {''} else { "--features " + features } }} -- --ignored - cargo test --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host test_violate_seccomp_filters --no-default-features {{ if features =~"mshv2" {"--features init-paging,mshv2"} else {"--features mshv3,init-paging,kvm" } }} --lib -- --ignored + {{ cargo-cmd }} test --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host test_violate_seccomp_filters --lib {{ if features =="" {''} else { "--features " + features } }} -- --ignored + {{ cargo-cmd }} test --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host test_violate_seccomp_filters --no-default-features {{ if features =~"mshv2" {"--features init-paging,mshv2"} else {"--features mshv3,init-paging,kvm" } }} --lib -- --ignored # tests compilation with no default features on different platforms test-compilation-no-default-features target=default-target: @# Linux should fail without a hypervisor feature (kvm, mshv, or mshv3) - {{ if os() == "linux" { "! cargo check -p hyperlight-host --no-default-features 2> /dev/null" } else { "" } }} + {{ if os() == "linux" { "! " + cargo-cmd + " check -p hyperlight-host --no-default-features "+target-triple-flag+" 2> /dev/null" } else { "" } }} @# Windows should succeed even without default features - {{ if os() == "windows" { "cargo check -p hyperlight-host --no-default-features" } else { "" } }} + {{ if os() == "windows" { cargo-cmd + " check -p hyperlight-host --no-default-features" } else { "" } }} @# Linux should succeed with a hypervisor driver but without init-paging - {{ if os() == "linux" { "cargo check -p hyperlight-host --no-default-features --features kvm" } else { "" } }} - {{ if os() == "linux" { "cargo check -p hyperlight-host --no-default-features --features mshv2" } else { "" } }} - {{ if os() == "linux" { "cargo check -p hyperlight-host --no-default-features --features mshv3" } else { "" } }} + {{ if os() == "linux" { cargo-cmd + " check -p hyperlight-host --no-default-features --features kvm" } else { "" } }} {{ target-triple-flag }} + {{ if os() == "linux" { cargo-cmd + " check -p hyperlight-host --no-default-features --features mshv2" } else { "" } }} {{ target-triple-flag }} + {{ if os() == "linux" { cargo-cmd + " check -p hyperlight-host --no-default-features --features mshv3" } else { "" } }} {{ target-triple-flag }} # runs tests that exercise gdb debugging test-rust-gdb-debugging target=default-target features="": - cargo test --profile={{ if target == "debug" { "dev" } else { target } }} --example guest-debugging {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }} - cargo test --profile={{ if target == "debug" { "dev" } else { target } }} {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }} -- test_gdb + {{ cargo-cmd }} test --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --example guest-debugging {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }} + {{ cargo-cmd }} test --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }} -- test_gdb # rust test for crashdump test-rust-crashdump target=default-target features="": - cargo test --profile={{ if target == "debug" { "dev" } else { target } }} {{ if features =="" {'--features crashdump'} else { "--features crashdump," + features } }} -- test_crashdump + {{ cargo-cmd }} test --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} {{ if features =="" {'--features crashdump'} else { "--features crashdump," + features } }} -- test_crashdump # rust test for tracing test-rust-tracing target=default-target features="": # Run tests for the tracing guest and macro - cargo test -p hyperlight-guest-tracing --profile={{ if target == "debug" { "dev" } else { target } }} - cargo test -p hyperlight-guest-tracing-macro --profile={{ if target == "debug" { "dev" } else { target } }} + {{ cargo-cmd }} test -p hyperlight-guest-tracing --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} + {{ cargo-cmd }} test -p hyperlight-guest-tracing-macro --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} # Prepare the tracing guest for testing just build-rust-guests {{ target }} trace_guest just move-rust-guests {{ target }} # Run hello-world example with tracing enabled to get the trace output - TRACE_OUTPUT="$(cargo run --profile={{ if target == "debug" { "dev" } else { target } }} --example hello-world --features {{ if features =="" {"trace_guest"} else { "trace_guest," + features } }})" && \ + # note that trace-dump doesn't run on MUSL target as of now + TRACE_OUTPUT="$({{ cargo-cmd }} run --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --example hello-world --features {{ if features =="" {"trace_guest"} else { "trace_guest," + features } }})" && \ TRACE_FILE="$(echo "$TRACE_OUTPUT" | grep -oE 'Creating trace file at: [^ ]+' | awk -F': ' '{print $2}')" && \ echo "$TRACE_OUTPUT" && \ if [ -z "$TRACE_FILE" ]; then \ @@ -215,13 +222,18 @@ test-rust-tracing target=default-target features="": just move-rust-guests {{ target }} test-doc target=default-target features="": - cargo test --profile={{ if target == "debug" { "dev" } else { target } }} {{ if features =="" {''} else { "--features " + features } }} --doc + {{ cargo-cmd }} test --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} {{ if features =="" {''} else { "--features " + features } }} --doc + ################ ### LINTING #### ################ check: - cargo check + {{ cargo-cmd }} check {{ target-triple-flag }} + {{ cargo-cmd }} check -p hyperlight-host --features crashdump {{ target-triple-flag }} + {{ cargo-cmd }} check -p hyperlight-host --features print_debug {{ target-triple-flag }} + {{ cargo-cmd }} check -p hyperlight-host --features gdb {{ target-triple-flag }} + {{ cargo-cmd }} check -p hyperlight-host --features trace_guest,unwind_guest,mem_profile {{ target-triple-flag }} fmt-check: cargo +nightly fmt --all -- --check @@ -241,7 +253,7 @@ fmt-apply: cargo +nightly fmt --manifest-path src/hyperlight_guest_capi/Cargo.toml clippy target=default-target: (witguest-wit) - cargo clippy --all-targets --all-features --profile={{ if target == "debug" { "dev" } else { target } }} -- -D warnings + {{ cargo-cmd }} clippy --all-targets --all-features --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -- -D warnings clippy-guests target=default-target: (witguest-wit) cd src/tests/rust_guests/simpleguest && cargo clippy --profile={{ if target == "debug" { "dev" } else { target } }} -- -D warnings @@ -255,11 +267,11 @@ clippy-apply-fix-windows: # Run clippy with feature combinations for all packages clippy-exhaustive target=default-target: (witguest-wit) - ./hack/clippy-package-features.sh hyperlight-host {{ target }} - ./hack/clippy-package-features.sh hyperlight-guest {{ target }} + ./hack/clippy-package-features.sh hyperlight-host {{ target }} {{ target-triple }} + ./hack/clippy-package-features.sh hyperlight-guest {{ target }} ./hack/clippy-package-features.sh hyperlight-guest-bin {{ target }} - ./hack/clippy-package-features.sh hyperlight-common {{ target }} - ./hack/clippy-package-features.sh hyperlight-testing {{ target }} + ./hack/clippy-package-features.sh hyperlight-common {{ target }} {{ target-triple }} + ./hack/clippy-package-features.sh hyperlight-testing {{ target }} {{ target-triple }} just clippy-guests {{ target }} # Test a specific package with all feature combinations @@ -275,14 +287,14 @@ verify-msrv: ##################### run-rust-examples target=default-target features="": - cargo run --profile={{ if target == "debug" { "dev" } else { target } }} --example metrics {{ if features =="" {''} else { "--features " + features } }} - cargo run --profile={{ if target == "debug" { "dev" } else { target } }} --example metrics {{ if features =="" {"--features function_call_metrics"} else {"--features function_call_metrics," + features} }} - cargo run --profile={{ if target == "debug" { "dev" } else { target } }} --example logging {{ if features =="" {''} else { "--features " + features } }} + {{ cargo-cmd }} run --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --example metrics {{ if features =="" {''} else { "--features " + features } }} + {{ cargo-cmd }} run --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --example metrics {{ if features =="" {"--features function_call_metrics"} else {"--features function_call_metrics," + features} }} + {{ cargo-cmd }} run --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --example logging {{ if features =="" {''} else { "--features " + features } }} # The two tracing examples are flaky on windows so we run them on linux only for now, need to figure out why as they run fine locally on windows run-rust-examples-linux target=default-target features="": (run-rust-examples target features) - cargo run --profile={{ if target == "debug" { "dev" } else { target } }} --example tracing {{ if features =="" {''} else { "--features " + features } }} - cargo run --profile={{ if target == "debug" { "dev" } else { target } }} --example tracing {{ if features =="" {"--features function_call_metrics" } else {"--features function_call_metrics," + features} }} + {{ cargo-cmd }} run --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --example tracing {{ if features =="" {''} else { "--features " + features } }} + {{ cargo-cmd }} run --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --example tracing {{ if features =="" {"--features function_call_metrics" } else {"--features function_call_metrics," + features} }} ######################### diff --git a/hack/clippy-package-features.sh b/hack/clippy-package-features.sh index dddf582d8..e769700ff 100755 --- a/hack/clippy-package-features.sh +++ b/hack/clippy-package-features.sh @@ -4,13 +4,23 @@ set -euo pipefail # Check for required arguments if [[ $# -lt 2 ]]; then - echo "Usage: $0 " >&2 - echo "Example: $0 hyperlight-host debug" >&2 + echo "Usage: $0 [target_triple]" >&2 + echo "Example: $0 hyperlight-host debug x86_64-unknown-linux-musl" >&2 exit 1 fi PACKAGE="$1" TARGET="$2" +TARGET_TRIPLE="${3:-}" + +CARGO="cargo" + +# Cargo target argument to append to cargo calls (empty if not provided) +TRIPLE_ARG="" +if [[ -n "${TARGET_TRIPLE}" ]]; then + TRIPLE_ARG="--target ${TARGET_TRIPLE} --target-dir ./target/host" + CARGO="cross" +fi # Convert target for cargo profile PROFILE=$([ "$TARGET" = "debug" ] && echo "dev" || echo "$TARGET") @@ -38,23 +48,23 @@ fi # Test with minimal features if [[ ${#REQUIRED_FEATURES[@]} -gt 0 ]]; then echo "Testing $PACKAGE with required features only ($required_features_str)..." - (set -x; cargo clippy -p "$PACKAGE" --all-targets --no-default-features --features "$required_features_str" --profile="$PROFILE" -- -D warnings) + (set -x; "$CARGO" clippy -p "$PACKAGE" --all-targets --no-default-features --features "$required_features_str" --profile="$PROFILE" ${TRIPLE_ARG} -- -D warnings) else echo "Testing $PACKAGE with no features..." - (set -x; cargo clippy -p "$PACKAGE" --all-targets --no-default-features --profile="$PROFILE" -- -D warnings) + (set -x; "$CARGO" clippy -p "$PACKAGE" --all-targets --no-default-features --profile="$PROFILE" ${TRIPLE_ARG} -- -D warnings) fi echo "Testing $PACKAGE with default features..." -(set -x; cargo clippy -p "$PACKAGE" --all-targets --profile="$PROFILE" -- -D warnings) +(set -x; "$CARGO" clippy -p "$PACKAGE" --all-targets --profile="$PROFILE" ${TRIPLE_ARG} -- -D warnings) # Test each additional feature individually for feature in $features; do if [[ ${#REQUIRED_FEATURES[@]} -gt 0 ]]; then echo "Testing $PACKAGE with feature: $required_features_str,$feature" - (set -x; cargo clippy -p "$PACKAGE" --all-targets --no-default-features --features "$required_features_str,$feature" --profile="$PROFILE" -- -D warnings) + (set -x; "$CARGO" clippy -p "$PACKAGE" --all-targets --no-default-features --features "$required_features_str,$feature" --profile="$PROFILE" ${TRIPLE_ARG} -- -D warnings) else echo "Testing $PACKAGE with feature: $feature" - (set -x; cargo clippy -p "$PACKAGE" --all-targets --no-default-features --features "$feature" --profile="$PROFILE" -- -D warnings) + (set -x; "$CARGO" clippy -p "$PACKAGE" --all-targets --no-default-features --features "$feature" --profile="$PROFILE" ${TRIPLE_ARG} -- -D warnings) fi done @@ -63,9 +73,9 @@ if [[ -n "$features" ]]; then all_features=$(echo $features | tr '\n' ',' | sed 's/,$//') if [[ ${#REQUIRED_FEATURES[@]} -gt 0 ]]; then echo "Testing $PACKAGE with all features: $required_features_str,$all_features" - (set -x; cargo clippy -p "$PACKAGE" --all-targets --no-default-features --features "$required_features_str,$all_features" --profile="$PROFILE" -- -D warnings) + (set -x; "$CARGO" clippy -p "$PACKAGE" --all-targets --no-default-features --features "$required_features_str,$all_features" --profile="$PROFILE" ${TRIPLE_ARG} -- -D warnings) else echo "Testing $PACKAGE with all features: $all_features" - (set -x; cargo clippy -p "$PACKAGE" --all-targets --no-default-features --features "$all_features" --profile="$PROFILE" -- -D warnings) + (set -x; "$CARGO" clippy -p "$PACKAGE" --all-targets --no-default-features --features "$all_features" --profile="$PROFILE" ${TRIPLE_ARG} -- -D warnings) fi fi \ No newline at end of file diff --git a/src/hyperlight_host/src/mem/shared_mem.rs b/src/hyperlight_host/src/mem/shared_mem.rs index 84cbb4c8e..030c2c958 100644 --- a/src/hyperlight_host/src/mem/shared_mem.rs +++ b/src/hyperlight_host/src/mem/shared_mem.rs @@ -1230,10 +1230,17 @@ mod tests { #[test] fn guard_page_testing_shim() { let tests = vec!["read", "write", "exec"]; - for test in tests { + let triple = std::env::var("TARGET_TRIPLE").ok(); + let target_args = if let Some(triple) = triple.filter(|t| !t.is_empty()) { + vec!["--target".to_string(), triple.to_string()] + } else { + vec![] + }; let status = std::process::Command::new("cargo") - .args(["test", "-p", "hyperlight-host", "--", "--ignored", test]) + .args(["test", "-p", "hyperlight-host"]) + .args(target_args) + .args(["--", "--ignored", test]) .stdin(std::process::Stdio::null()) .stdout(std::process::Stdio::null()) .stderr(std::process::Stdio::null()) From 158c55b1499937f76c970f060e12253a70df19c4 Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Fri, 29 Aug 2025 22:51:13 +0000 Subject: [PATCH 2/9] Disable seccomp for musl target Signed-off-by: James Sturtevant --- src/hyperlight_host/build.rs | 1 + src/hyperlight_host/src/error.rs | 6 +++--- src/hyperlight_host/src/func/host_functions.rs | 4 ++-- src/hyperlight_host/src/lib.rs | 2 +- src/hyperlight_host/src/metrics/mod.rs | 4 ++-- src/hyperlight_host/src/sandbox/host_funcs.rs | 4 ++-- .../src/sandbox/initialized_multi_use.rs | 10 +++++----- src/hyperlight_host/src/sandbox/uninitialized.rs | 12 ++++++------ src/hyperlight_host/src/signal_handlers/mod.rs | 4 ++-- .../src/signal_handlers/sigsys_signal_handler.rs | 2 +- src/hyperlight_host/tests/integration_test.rs | 4 ++-- 11 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/hyperlight_host/build.rs b/src/hyperlight_host/build.rs index 6c90a485f..bef001b22 100644 --- a/src/hyperlight_host/build.rs +++ b/src/hyperlight_host/build.rs @@ -101,6 +101,7 @@ fn main() -> Result<()> { // the other features they want. mshv2: { all(feature = "mshv2", target_os = "linux") }, mshv3: { all(feature = "mshv3", not(feature="mshv2"), target_os = "linux") }, + seccomp: { all(feature = "seccomp", target_os = "linux", not(target_env = "musl")) }, } #[cfg(feature = "build-metadata")] diff --git a/src/hyperlight_host/src/error.rs b/src/hyperlight_host/src/error.rs index d2fb382a0..9ecce9968 100644 --- a/src/hyperlight_host/src/error.rs +++ b/src/hyperlight_host/src/error.rs @@ -71,7 +71,7 @@ pub enum HyperlightError { /// A disallowed syscall was caught #[error("Seccomp filter trapped on disallowed syscall (check STDERR for offending syscall)")] - #[cfg(all(feature = "seccomp", target_os = "linux"))] + #[cfg(seccomp)] DisallowedSyscall, /// A generic error with a message @@ -218,12 +218,12 @@ pub enum HyperlightError { /// a backend error occurred with seccomp filters #[error("Backend Error with Seccomp Filter {0:?}")] - #[cfg(all(feature = "seccomp", target_os = "linux"))] + #[cfg(seccomp)] SeccompFilterBackendError(#[from] seccompiler::BackendError), /// an error occurred with seccomp filters #[error("Error with Seccomp Filter {0:?}")] - #[cfg(all(feature = "seccomp", target_os = "linux"))] + #[cfg(seccomp)] SeccompFilterError(#[from] seccompiler::Error), /// Tried to restore snapshot to a sandbox that is not the same as the one the snapshot was taken from diff --git a/src/hyperlight_host/src/func/host_functions.rs b/src/hyperlight_host/src/func/host_functions.rs index 3944b0703..ed11914f1 100644 --- a/src/hyperlight_host/src/func/host_functions.rs +++ b/src/hyperlight_host/src/func/host_functions.rs @@ -35,7 +35,7 @@ pub trait Registerable { ) -> Result<()>; /// Register a primitive host function whose worker thread has /// extra permissive seccomp filters installed - #[cfg(all(feature = "seccomp", target_os = "linux"))] + #[cfg(seccomp)] fn register_host_function_with_syscalls( &mut self, name: &str, @@ -63,7 +63,7 @@ impl Registerable for UninitializedSandbox { (*hfs).register_host_function(name.to_string(), entry, self.mgr.unwrap_mgr_mut()) } - #[cfg(all(feature = "seccomp", target_os = "linux"))] + #[cfg(seccomp)] fn register_host_function_with_syscalls( &mut self, name: &str, diff --git a/src/hyperlight_host/src/lib.rs b/src/hyperlight_host/src/lib.rs index e1cc2e5a2..b4b8de231 100644 --- a/src/hyperlight_host/src/lib.rs +++ b/src/hyperlight_host/src/lib.rs @@ -76,7 +76,7 @@ pub mod metrics; /// outside this file. Types from this module needed for public consumption are /// re-exported below. pub mod sandbox; -#[cfg(all(feature = "seccomp", target_os = "linux"))] +#[cfg(seccomp)] pub(crate) mod seccomp; /// Signal handling for Linux #[cfg(target_os = "linux")] diff --git a/src/hyperlight_host/src/metrics/mod.rs b/src/hyperlight_host/src/metrics/mod.rs index 76e9b93a3..67a7b0eca 100644 --- a/src/hyperlight_host/src/metrics/mod.rs +++ b/src/hyperlight_host/src/metrics/mod.rs @@ -133,7 +133,7 @@ mod tests { if #[cfg(feature = "function_call_metrics")] { use metrics::Label; - let expected_num_metrics = if cfg!(all(feature = "seccomp", target_os = "linux")) { + let expected_num_metrics = if cfg!(all(seccomp)) { 3 // if seccomp enabled, the host call duration metric is emitted on a separate thread which this local recorder doesn't capture } else { 4 @@ -186,7 +186,7 @@ mod tests { "Histogram metric does not match expected value" ); - if !cfg!(all(feature = "seccomp", target_os = "linux")) { + if !cfg!(all(seccomp)) { // 4. Host call duration let histogram_key = CompositeKey::new( metrics_util::MetricKind::Histogram, diff --git a/src/hyperlight_host/src/sandbox/host_funcs.rs b/src/hyperlight_host/src/sandbox/host_funcs.rs index 6d3c8d98a..231630552 100644 --- a/src/hyperlight_host/src/sandbox/host_funcs.rs +++ b/src/hyperlight_host/src/sandbox/host_funcs.rs @@ -154,7 +154,7 @@ pub(super) fn default_writer_func(s: String) -> Result { } } -#[cfg(all(feature = "seccomp", target_os = "linux"))] +#[cfg(seccomp)] fn maybe_with_seccomp( name: &str, syscalls: Option<&[ExtraAllowedSyscall]>, @@ -199,7 +199,7 @@ fn maybe_with_seccomp( }) } -#[cfg(not(all(feature = "seccomp", target_os = "linux")))] +#[cfg(not(all(seccomp)))] fn maybe_with_seccomp( _name: &str, _syscalls: Option<&[ExtraAllowedSyscall]>, diff --git a/src/hyperlight_host/src/sandbox/initialized_multi_use.rs b/src/hyperlight_host/src/sandbox/initialized_multi_use.rs index b1b41fff2..29b7669c9 100644 --- a/src/hyperlight_host/src/sandbox/initialized_multi_use.rs +++ b/src/hyperlight_host/src/sandbox/initialized_multi_use.rs @@ -643,7 +643,7 @@ mod tests { let res: Result = sbox.call("ViolateSeccompFilters", ()); - #[cfg(feature = "seccomp")] + #[cfg(seccomp)] match res { Ok(_) => panic!("Expected to fail due to seccomp violation"), Err(e) => match e { @@ -652,7 +652,7 @@ mod tests { }, } - #[cfg(not(feature = "seccomp"))] + #[cfg(not(seccomp))] match res { Ok(_) => (), Err(e) => panic!("Expected to succeed without seccomp: {}", e), @@ -660,7 +660,7 @@ mod tests { } // Second, run with allowing `SYS_getpid` - #[cfg(feature = "seccomp")] + #[cfg(seccomp)] { let mut usbox = UninitializedSandbox::new( GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")), @@ -737,7 +737,7 @@ mod tests { ) .expect("Expected to call host function that returns i64"); - if cfg!(feature = "seccomp") { + if cfg!(seccomp) { // If seccomp is enabled, we expect the syscall to return EACCES, as setup by our seccomp filter assert_eq!(host_func_result, -libc::EACCES as i64); } else { @@ -746,7 +746,7 @@ mod tests { } } - #[cfg(feature = "seccomp")] + #[cfg(seccomp)] { // Now let's make sure if we register the `openat` syscall as an extra allowed syscall, it will succeed let mut ubox = UninitializedSandbox::new( diff --git a/src/hyperlight_host/src/sandbox/uninitialized.rs b/src/hyperlight_host/src/sandbox/uninitialized.rs index 6d905cf52..0083abd2b 100644 --- a/src/hyperlight_host/src/sandbox/uninitialized.rs +++ b/src/hyperlight_host/src/sandbox/uninitialized.rs @@ -36,7 +36,7 @@ use crate::mem::shared_mem::ExclusiveSharedMemory; use crate::sandbox::SandboxConfiguration; use crate::{MultiUseSandbox, Result, new_error}; -#[cfg(all(target_os = "linux", feature = "seccomp"))] +#[cfg(seccomp)] const EXTRA_ALLOWED_SYSCALLS_FOR_WRITER_FUNC: &[super::ExtraAllowedSyscall] = &[ // Fuzzing fails without `mmap` being an allowed syscall on our seccomp filter. // All fuzzing does is call `PrintOutput` (which calls `HostPrint` ). Thing is, `println!` @@ -325,7 +325,7 @@ impl UninitializedSandbox { /// /// Unlike [`register`](Self::register), this variant allows specifying extra syscalls /// that will be permitted when the function handler runs. - #[cfg(all(feature = "seccomp", target_os = "linux"))] + #[cfg(seccomp)] pub fn register_with_extra_allowed_syscalls< Args: ParameterTuple, Output: SupportedReturnType, @@ -348,10 +348,10 @@ impl UninitializedSandbox { &mut self, print_func: impl Into>, ) -> Result<()> { - #[cfg(not(all(target_os = "linux", feature = "seccomp")))] + #[cfg(not(seccomp))] self.register("HostPrint", print_func)?; - #[cfg(all(target_os = "linux", feature = "seccomp"))] + #[cfg(seccomp)] self.register_with_extra_allowed_syscalls( "HostPrint", print_func, @@ -365,13 +365,13 @@ impl UninitializedSandbox { /// /// Like [`register_print`](Self::register_print), but allows specifying extra syscalls /// that will be permitted during function execution. - #[cfg(all(feature = "seccomp", target_os = "linux"))] + #[cfg(seccomp)] pub fn register_print_with_extra_allowed_syscalls( &mut self, print_func: impl Into>, extra_allowed_syscalls: impl IntoIterator, ) -> Result<()> { - #[cfg(all(target_os = "linux", feature = "seccomp"))] + #[cfg(seccomp)] self.register_with_extra_allowed_syscalls( "HostPrint", print_func, diff --git a/src/hyperlight_host/src/signal_handlers/mod.rs b/src/hyperlight_host/src/signal_handlers/mod.rs index 1644a7206..43a58977b 100644 --- a/src/hyperlight_host/src/signal_handlers/mod.rs +++ b/src/hyperlight_host/src/signal_handlers/mod.rs @@ -18,7 +18,7 @@ use libc::c_int; use crate::sandbox::SandboxConfiguration; -#[cfg(feature = "seccomp")] +#[cfg(seccomp)] pub mod sigsys_signal_handler; pub(crate) fn setup_signal_handlers(config: &SandboxConfiguration) -> crate::Result<()> { @@ -27,7 +27,7 @@ pub(crate) fn setup_signal_handlers(config: &SandboxConfiguration) -> crate::Res // Anything that performs memory allocations, locks, and others are non-async-signal-safe. // Hyperlight signal handlers are all designed to be async-signal-safe, so this function // should be safe to call. - #[cfg(feature = "seccomp")] + #[cfg(seccomp)] { use std::sync::Once; diff --git a/src/hyperlight_host/src/signal_handlers/sigsys_signal_handler.rs b/src/hyperlight_host/src/signal_handlers/sigsys_signal_handler.rs index 1bab82e1e..0202873f5 100644 --- a/src/hyperlight_host/src/signal_handlers/sigsys_signal_handler.rs +++ b/src/hyperlight_host/src/signal_handlers/sigsys_signal_handler.rs @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -#[cfg(feature = "seccomp")] +#[cfg(seccomp)] pub(super) extern "C" fn handle_sigsys( signal: i32, info: *mut libc::siginfo_t, diff --git a/src/hyperlight_host/tests/integration_test.rs b/src/hyperlight_host/tests/integration_test.rs index 8b2077a19..be3ead190 100644 --- a/src/hyperlight_host/tests/integration_test.rs +++ b/src/hyperlight_host/tests/integration_test.rs @@ -49,10 +49,10 @@ fn interrupt_host_call() { Ok(()) }; - #[cfg(any(target_os = "windows", not(feature = "seccomp")))] + #[cfg(any(target_os = "windows", not(seccomp)))] usbox.register("Spin", spin).unwrap(); - #[cfg(all(target_os = "linux", feature = "seccomp"))] + #[cfg(seccomp)] usbox .register_with_extra_allowed_syscalls("Spin", spin, vec![libc::SYS_clock_nanosleep]) .unwrap(); From 8356e444da3f49a9a54cd816443faac4375538bb Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Fri, 29 Aug 2025 23:58:48 +0000 Subject: [PATCH 3/9] Fix a race condition in this test Signed-off-by: James Sturtevant --- src/hyperlight_host/examples/guest-debugging/main.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/hyperlight_host/examples/guest-debugging/main.rs b/src/hyperlight_host/examples/guest-debugging/main.rs index e414b72d8..973ff4d70 100644 --- a/src/hyperlight_host/examples/guest-debugging/main.rs +++ b/src/hyperlight_host/examples/guest-debugging/main.rs @@ -151,6 +151,18 @@ mod tests { #[cfg(not(mshv2))] let features = "gdb"; + // build it before running to avoid a race condition below + let mut guest_child = Command::new("cargo") + .arg("build") + .arg("--example") + .arg("guest-debugging") + .arg("--features") + .arg(features) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .status() + .map_err(|e| new_error!("Failed to build guest process: {}", e))?; + let mut guest_child = Command::new("cargo") .arg("run") .arg("--example") From fd1221476156229cb10d98a661e8026fc79d835a Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Thu, 11 Sep 2025 02:38:32 +0000 Subject: [PATCH 4/9] Refactor dep_rust so it is re-usable in new nightly job Signed-off-by: James Sturtevant --- .github/workflows/RustNightly.yml | 27 ++++++ .github/workflows/ValidatePullRequest.yml | 9 ++ .github/workflows/dep_rust.yml | 105 +++++++++++++--------- 3 files changed, 98 insertions(+), 43 deletions(-) create mode 100644 .github/workflows/RustNightly.yml diff --git a/.github/workflows/RustNightly.yml b/.github/workflows/RustNightly.yml new file mode 100644 index 000000000..ad4604bad --- /dev/null +++ b/.github/workflows/RustNightly.yml @@ -0,0 +1,27 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json + +name: Nightly + +on: + workflow_dispatch: + schedule: + - cron: '0 0 */2 * *' + +jobs: + musl: + strategy: + fail-fast: true + matrix: + hypervisor: [kvm, mshv3] + cpu: [amd, intel] + config: [debug, release] + uses: ./.github/workflows/dep_rust.yml + secrets: inherit + with: + hypervisor: ${{ matrix.hypervisor }} + cpu: ${{ matrix.cpu }} + config: ${{ matrix.config }} + target_triple: x86_64-unknown-linux-musl + +# TODO: using notification script + diff --git a/.github/workflows/ValidatePullRequest.yml b/.github/workflows/ValidatePullRequest.yml index e6b07ddac..2a3977848 100644 --- a/.github/workflows/ValidatePullRequest.yml +++ b/.github/workflows/ValidatePullRequest.yml @@ -44,10 +44,19 @@ jobs: rust: needs: - docs-pr + strategy: + fail-fast: true + matrix: + hypervisor: [hyperv, 'hyperv-ws2025', mshv, mshv3, kvm] + cpu: [amd, intel] + config: [debug, release] uses: ./.github/workflows/dep_rust.yml secrets: inherit with: docs_only: ${{needs.docs-pr.outputs.docs-only}} + hypervisor: ${{ matrix.hypervisor }} + cpu: ${{ matrix.cpu }} + config: ${{ matrix.config }} fuzzing: needs: diff --git a/.github/workflows/dep_rust.yml b/.github/workflows/dep_rust.yml index 25777ca43..e7875812d 100644 --- a/.github/workflows/dep_rust.yml +++ b/.github/workflows/dep_rust.yml @@ -11,6 +11,26 @@ on: required: false type: string default: "false" + target_triple: + description: Target triple for cross-compilation + required: false + type: string + default: "" + hypervisor: + description: Hypervisor for this run (passed from caller matrix) + required: false + type: string + default: "kvm" + config: + description: Build configuration for this run (passed from caller matrix) + required: false + type: string + default: "debug" + cpu: + description: CPU architecture for the build (passed from caller matrix) + required: false + type: string + default: "amd" env: CARGO_TERM_COLOR: always @@ -32,17 +52,12 @@ defaults: jobs: code-checks: - if: ${{ inputs.docs_only == 'false' }} + if: ${{ inputs.docs_only == 'false' && (inputs.hypervisor == 'hyperv-ws2025' || inputs.hypervisor == 'kvm') }} timeout-minutes: 60 - strategy: - fail-fast: true - matrix: - hypervisor: ['hyperv-ws2025', kvm] - config: [debug, release] runs-on: ${{ fromJson( format('["self-hosted", "{0}", "X64", "1ES.Pool=hld-{1}-amd"]', - (matrix.hypervisor == 'hyperv-ws2025') && 'Windows' || 'Linux', - matrix.hypervisor == 'hyperv-ws2025' && 'win2025' || 'kvm')) }} + (inputs.hypervisor == 'hyperv-ws2025') && 'Windows' || 'Linux', + inputs.hypervisor == 'hyperv-ws2025' && 'win2025' || 'kvm')) }} steps: - uses: actions/checkout@v5 @@ -63,13 +78,17 @@ jobs: - name: clippy if: ${{ (runner.os == 'Windows' )}} run: | - just clippy ${{ matrix.config }} - just clippy-guests ${{ matrix.config }} + just clippy ${{ inputs.config }} + just clippy-guests ${{ inputs.config }} + env: + TARGET_TRIPLE: ${{ inputs.target_triple }} - name: clippy exhaustive check if: ${{ (runner.os == 'Linux' )}} run: | - just clippy-exhaustive ${{ matrix.config }} + just clippy-exhaustive ${{ inputs.config }} + env: + TARGET_TRIPLE: ${{ inputs.target_triple }} - name: Verify MSRV run: ./dev/verify-msrv.sh hyperlight-host hyperlight-guest hyperlight-guest-bin hyperlight-common @@ -77,18 +96,11 @@ jobs: build: if: ${{ inputs.docs_only == 'false' }} timeout-minutes: 60 - strategy: - fail-fast: true - matrix: - hypervisor: [hyperv, 'hyperv-ws2025', mshv, mshv3, kvm] # hyperv is windows, mshv and kvm are linux - cpu: [amd, intel] - config: [debug, release] - runs-on: ${{ fromJson( format('["self-hosted", "{0}", "X64", "1ES.Pool=hld-{1}-{2}"]', - (matrix.hypervisor == 'hyperv' || matrix.hypervisor == 'hyperv-ws2025') && 'Windows' || 'Linux', - matrix.hypervisor == 'hyperv' && 'win2022' || matrix.hypervisor == 'hyperv-ws2025' && 'win2025' || matrix.hypervisor == 'mshv3' && 'azlinux3-mshv' || matrix.hypervisor, - matrix.cpu)) }} + (inputs.hypervisor == 'hyperv' || inputs.hypervisor == 'hyperv-ws2025') && 'Windows' || 'Linux', + inputs.hypervisor == 'hyperv' && 'win2022' || inputs.hypervisor == 'hyperv-ws2025' && 'win2025' || inputs.hypervisor == 'mshv3' && 'azlinux3-mshv' || inputs.hypervisor, + inputs.cpu)) }} steps: - uses: actions/checkout@v5 @@ -112,35 +124,36 @@ jobs: - name: Build and move Rust guests run: | # use these commands in favor of build-and-move-rust-guests to avoid building both configs - just build-rust-guests ${{ matrix.config }} - just move-rust-guests ${{ matrix.config }} + just build-rust-guests ${{ inputs.config }} + just move-rust-guests ${{ inputs.config }} - name: Build c guests run: | # use these commands in favor of build-and-move-c-guests to avoid building both configs - just build-c-guests ${{ matrix.config }} - just move-c-guests ${{ matrix.config }} + just build-c-guests ${{ inputs.config }} + just move-c-guests ${{ inputs.config }} + - name: Build - run: just build ${{ matrix.config }} + run: just build ${{ inputs.config }} + env: + TARGET_TRIPLE: ${{ inputs.target_triple }} - name: Run Rust tests env: CARGO_TERM_COLOR: always + TARGET_TRIPLE: ${{ inputs.target_triple }} run: | # with default features - just test ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv' && 'mshv2' || ''}} + just test ${{ inputs.config }} ${{ inputs.hypervisor == 'mshv' && 'mshv2' || '""'}} # with only one driver enabled (driver mshv/kvm feature is ignored on windows) + seccomp - just test ${{ matrix.config }} seccomp,${{ matrix.hypervisor == 'mshv' && 'mshv2' || matrix.hypervisor == 'mshv3' && 'mshv3' || 'kvm' }} + just test ${{ inputs.config }} seccomp,${{ inputs.hypervisor == 'mshv' && 'mshv2' || inputs.hypervisor == 'mshv3' && 'mshv3' || 'kvm' }} # make sure certain cargo features compile - cargo check -p hyperlight-host --features crashdump - cargo check -p hyperlight-host --features print_debug - cargo check -p hyperlight-host --features gdb - cargo check -p hyperlight-host --features trace_guest,unwind_guest,mem_profile + just check # without any features - just test-compilation-no-default-features ${{ matrix.config }} + just test-compilation-no-default-features ${{ inputs.config }} # One of the examples is flaky on Windows GH runners, so this allows us to disable it for now - name: Run Rust examples - windows @@ -148,43 +161,49 @@ jobs: env: CARGO_TERM_COLOR: always RUST_LOG: debug - run: just run-rust-examples ${{ matrix.config }} + TARGET_TRIPLE: ${{ inputs.target_triple }} + run: just run-rust-examples ${{ inputs.config }} + - name: Run Rust examples - linux - if: ${{ (runner.os != 'Windows') }} + if: false env: CARGO_TERM_COLOR: always RUST_LOG: debug - run: just run-rust-examples-linux ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv' && 'mshv2' || ''}} + TARGET_TRIPLE: ${{ inputs.target_triple }} + run: just run-rust-examples-linux ${{ inputs.config }} ${{ inputs.hypervisor == 'mshv' && 'mshv2' || '""'}} - name: Run Rust Gdb tests - linux if: runner.os == 'Linux' env: CARGO_TERM_COLOR: always RUST_LOG: debug - run: just test-rust-gdb-debugging ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv' && 'mshv2' || ''}} + TARGET_TRIPLE: ${{ inputs.target_triple }} + run: just test-rust-gdb-debugging ${{ inputs.config }} ${{ inputs.hypervisor == 'mshv' && 'mshv2' || '""'}} - name: Run Rust Crashdump tests env: CARGO_TERM_COLOR: always RUST_LOG: debug - run: just test-rust-crashdump ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv' && 'mshv2' || ''}} + TARGET_TRIPLE: ${{ inputs.target_triple }} + run: just test-rust-crashdump ${{ inputs.config }} ${{ inputs.hypervisor == 'mshv' && 'mshv2' || '""'}} - name: Run Rust Tracing tests - linux if: runner.os == 'Linux' env: CARGO_TERM_COLOR: always RUST_LOG: debug - run: just test-rust-tracing ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv' && 'mshv2' || ''}} + TARGET_TRIPLE: ${{ inputs.target_triple }} + run: just test-rust-tracing ${{ inputs.config }} ${{ inputs.hypervisor == 'mshv' && 'mshv2' || '""'}} - name: Download benchmarks from "latest" - run: just bench-download ${{ runner.os }} ${{ matrix.hypervisor }} ${{ matrix.cpu}} dev-latest # compare to prerelease + run: just bench-download ${{ runner.os }} ${{ inputs.hypervisor }} ${{ inputs.cpu}} dev-latest # compare to prerelease env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true - if: ${{ matrix.config == 'release' }} + if: ${{ inputs.config == 'release' && inputs.target_triple == '' }} - name: Run benchmarks run: | - just bench-ci main ${{ matrix.hypervisor == 'mshv' && 'mshv2' || ''}} - if: ${{ matrix.config == 'release' }} + just bench-ci main ${{ inputs.hypervisor == 'mshv' && 'mshv2' || ''}} + if: ${{ inputs.config == 'release' && inputs.target_triple == '' }} From 059a8965f433b35b346e1681b675c57affbdb926 Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Wed, 10 Sep 2025 22:58:17 +0000 Subject: [PATCH 5/9] Refactor CI failure and add to nightly job Signed-off-by: James Sturtevant --- .github/workflows/Fuzzing.yml | 2 +- .github/workflows/RustNightly.yml | 14 ++- dev/notify-ci-failure.sh | 169 ++++++++++++++++++++++++++++++ dev/notify-fuzzing-failure.sh | 153 --------------------------- 4 files changed, 183 insertions(+), 155 deletions(-) create mode 100755 dev/notify-ci-failure.sh delete mode 100755 dev/notify-fuzzing-failure.sh diff --git a/.github/workflows/Fuzzing.yml b/.github/workflows/Fuzzing.yml index a3c19778a..d586a659a 100644 --- a/.github/workflows/Fuzzing.yml +++ b/.github/workflows/Fuzzing.yml @@ -28,6 +28,6 @@ jobs: uses: actions/checkout@v5 - name: Notify Fuzzing Failure - run: ./dev/notify-fuzzing-failure.sh "fuzz_host_print,fuzz_guest_call,fuzz_host_call" + run: ./dev/notify-ci-failure.sh --labels="area/fuzzing,kind/bug,area/testing,lifecycle/needs-review" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/RustNightly.yml b/.github/workflows/RustNightly.yml index ad4604bad..370c55225 100644 --- a/.github/workflows/RustNightly.yml +++ b/.github/workflows/RustNightly.yml @@ -23,5 +23,17 @@ jobs: config: ${{ matrix.config }} target_triple: x86_64-unknown-linux-musl -# TODO: using notification script + notify-failure: + runs-on: ubuntu-latest + needs: musl + if: always() && needs.musl.result == 'failure' + permissions: + issues: write + steps: + - name: Checkout code + uses: actions/checkout@v5 + - name: Notify Nightly Failure + run: ./dev/notify-ci-failure.sh --title="Nightly musl Failure - ${{ github.run_number }}" --labels="area/ci-periodics,area/testing,kind/bug,lifecycle/needs-review" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/dev/notify-ci-failure.sh b/dev/notify-ci-failure.sh new file mode 100755 index 000000000..69be564c1 --- /dev/null +++ b/dev/notify-ci-failure.sh @@ -0,0 +1,169 @@ +#!/bin/bash +set -e +set -u +set -o pipefail + +## DESCRIPTION: +## +# Generic notifier for CI job failures that can create or update GitHub issues. +## This script creates or updates GitHub issues when a jobs fail. +## It checks for existing open failure issues and either creates +## a new one or adds a comment to an existing one. +## +## PRE-REQS: +## +## This script assumes that the gh cli is installed and in the PATH +## and that there is a GitHub PAT in the GITHUB_TOKEN env var +## with the following permissions: +## - issues (read/write) +## or that the user is logged into the gh cli with an account with those permissions +## +## Usage examples: +## ./dev/notify-fuzzing-failure.sh +## ./dev/notify-fuzzing-failure.sh --title="Nightly Failure" --labels="area/testing,kind/bug" +## ./dev/notify-fuzzing-failure.sh --test +## Run this script locally like: +## GITHUB_REPOSITORY="fork/hyperlight" GITHUB_RUN_ID=1 ./dev/notify-fuzzing-failure.sh --title="Nightly Failure" --labels="area/testing,kind/bug" + +REPO="${GITHUB_REPOSITORY:-hyperlight-dev/hyperlight}" +WORKFLOW_RUN_URL="${GITHUB_SERVER_URL:-https://github.com}/${REPO}/actions/runs/${GITHUB_RUN_ID:-unknown}" +TEST_MODE=false +ISSUE_TITLE="" +LABELS="area/testing,kind/bug,area/fuzzing,lifecycle/needs-review" + +for arg in "$@"; do + case $arg in + --test) + TEST_MODE=true + shift + ;; + --title=*) + ISSUE_TITLE="${arg#*=}" + shift + ;; + --labels=*) + LABELS="${arg#*=}" + shift + ;; + *) + esac +done + +# Normalize labels into an array +IFS=',' read -r -a LABEL_ARRAY <<< "$LABELS" + +# Choose a label to search existing issues for; prefer the first label if present +SEARCH_LABEL="${LABEL_ARRAY[0]:-area/fuzzing}" + +# Build issue title if not provided +if [ -z "$ISSUE_TITLE" ]; then + ISSUE_TITLE="Job Failure - $(date '+%Y-%m-%d')" +fi + + +if [ "$TEST_MODE" = true ]; then + echo "✅ Running in test mode - script structure is valid" + echo "Would check for issues in $REPO" + echo "Workflow URL would be: $WORKFLOW_RUN_URL" + echo "Issue Title would be: $ISSUE_TITLE" + echo "Labels would be: $LABELS" + echo "Search Label would be: $SEARCH_LABEL" + exit 0 +fi + +# Extract owner and repo name from the repository +OWNER=$(echo "$REPO" | cut -d'/' -f1) +REPO_NAME=$(echo "$REPO" | cut -d'/' -f2) + +echo "Checking for existing issues in $REPO with label '$SEARCH_LABEL'..." +EXISTING_ISSUES=$(gh api graphql -f query=' + query($owner: String!, $repo: String!, $label: String!) { + repository(owner: $owner, name: $repo) { + issues(first: 10, states: OPEN, labels: [$label]) { + totalCount + nodes { + number + title + url + labels(first: 20) { + nodes { + name + } + } + } + } + } + }' -f owner="$OWNER" -f repo="$REPO_NAME" -f label="$SEARCH_LABEL" --jq '.data.repository.issues') || EXISTING_ISSUES="" + +FUZZING_ISSUES=$(echo "$EXISTING_ISSUES" | jq '.nodes[]' 2>/dev/null || echo "") +FUZZING_ISSUE_COUNT=0 +if [ -n "$FUZZING_ISSUES" ]; then + FUZZING_ISSUE_COUNT=$(echo "$FUZZING_ISSUES" | jq -s 'length' 2>/dev/null || echo "0") +fi + +echo "Found $FUZZING_ISSUE_COUNT existing issue(s) matching label '$SEARCH_LABEL'" + +if [ "$FUZZING_ISSUE_COUNT" -gt 0 ]; then + ISSUE_NUMBER=$(echo "$FUZZING_ISSUES" | jq -r '.number' | head -1) + ISSUE_URL=$(echo "$FUZZING_ISSUES" | jq -r '.url' | head -1) + if [ "$ISSUE_NUMBER" = "null" ] || [ -z "$ISSUE_NUMBER" ]; then + echo "⚠️ Could not parse issue number from search results; will create a new issue" + FUZZING_ISSUE_COUNT=0 + else + echo "Adding comment to existing issue #$ISSUE_NUMBER" + COMMENT_BODY="## Job Failed Again + +**Date:** $(date '+%Y-%m-%d %H:%M:%S UTC') +**Workflow Run:** [$WORKFLOW_RUN_URL]($WORKFLOW_RUN_URL) + +The scheduled job has failed again. Please check the workflow logs and artifacts for details." + + if gh issue comment "$ISSUE_NUMBER" --body "$COMMENT_BODY" --repo "$REPO"; then + echo "✅ Added comment to existing issue #$ISSUE_NUMBER: $ISSUE_URL" + exit 0 + else + echo "❌ Failed to add comment to existing issue. Will attempt to create a new issue instead." + FUZZING_ISSUE_COUNT=0 + fi + fi +fi + +if [ "$FUZZING_ISSUE_COUNT" -eq 0 ]; then + echo "No existing matching issues found. Creating a new issue..." + + ISSUE_BODY="## Job Failure Report + +**Date:** $(date '+%Y-%m-%d %H:%M:%S UTC') +**Workflow Run:** [$WORKFLOW_RUN_URL]($WORKFLOW_RUN_URL) + +### Details +The scheduled job failed during execution. This issue was automatically created to track the failure. Please check the workflow logs and any uploaded artifacts for more details. + +### Next Steps +- [ ] Review the workflow logs for error details +- [ ] Download and analyze any crash artifacts if available +- [ ] Determine the root cause of the failure +- [ ] Fix the underlying issue + +--- +*This issue was automatically created by the CI failure notification system.*" + + # Build label args for gh issue create + LABEL_ARGS=() + for lbl in "${LABEL_ARRAY[@]}"; do + LABEL_ARGS+=("--label" "$lbl") + done + + if ISSUE_URL=$(gh issue create \ + --title "$ISSUE_TITLE" \ + --body "$ISSUE_BODY" \ + "${LABEL_ARGS[@]}" \ + --repo "$REPO"); then + echo "✅ Created new issue: $ISSUE_URL" + else + echo "❌ Failed to create new issue" + exit 1 + fi +fi + +echo "Notification script completed successfully" \ No newline at end of file diff --git a/dev/notify-fuzzing-failure.sh b/dev/notify-fuzzing-failure.sh deleted file mode 100755 index f4a6ac04f..000000000 --- a/dev/notify-fuzzing-failure.sh +++ /dev/null @@ -1,153 +0,0 @@ -#!/bin/bash -set -e -set -u -set -o pipefail - -## DESCRIPTION: -## -## This script creates or updates GitHub issues when fuzzing jobs fail. -## It checks for existing open fuzzing failure issues and either creates -## a new one or adds a comment to an existing one. -## -## PRE-REQS: -## -## This script assumes that the gh cli is installed and in the PATH -## and that there is a GitHub PAT in the GITHUB_TOKEN env var -## with the following permissions: -## - issues (read/write) -## or that the user is logged into the gh cli with an account with those permissions -## -## Run this script locally like: -## GITHUB_REPOSITORY="fork/hyperlight" GITHUB_RUN_ID=1 ./dev/notify-fuzzing-failure.sh "fuzz_host_print,fuzz_guest_call,fuzz_host_call" - -REPO="${GITHUB_REPOSITORY:-hyperlight-dev/hyperlight}" -WORKFLOW_RUN_URL="${GITHUB_SERVER_URL:-https://github.com}/${REPO}/actions/runs/${GITHUB_RUN_ID:-unknown}" -FUZZING_TARGETS="${1:-unknown}" - -# Check if running in test mode (handle both first and second arguments) -if [ "${1:-}" = "--test" ] || [ "${2:-}" = "--test" ]; then - echo "✅ Running in test mode - script structure is valid" - echo "Would check for fuzzing failure issues in $REPO" - echo "Would create issue or comment for fuzzing targets: ${1:-unknown}" - echo "Workflow URL would be: $WORKFLOW_RUN_URL" - exit 0 -fi - -echo "Checking for existing fuzzing failure issues in $REPO..." - -# Extract owner and repo name from the repository -OWNER=$(echo "$REPO" | cut -d'/' -f1) -REPO_NAME=$(echo "$REPO" | cut -d'/' -f2) - -# Define the issue title and labels -ISSUE_TITLE="Fuzzing Job Failure - $(date '+%Y-%m-%d')" -TESTING_LABEL="area/testing" -FAILURE_LABEL="kind/bug" -FUZZING_LABEL="area/fuzzing" -LIFECYCLE_LABEL="lifecycle/needs-review" - -# Search for existing open fuzzing failure issues -echo "Searching for existing open fuzzing failure issues..." -EXISTING_ISSUES=$(gh api graphql -f query=' - query($owner: String!, $repo: String!) { - repository(owner: $owner, name: $repo) { - issues(first: 10, states: OPEN, labels: ["area/fuzzing"]) { - totalCount - nodes { - number - title - url - labels(first: 20) { - nodes { - name - } - } - } - } - } - }' -f owner="$OWNER" -f repo="$REPO_NAME" --jq '.data.repository.issues') - -# Filter for fuzzing-related issues (now all results should be fuzzing issues due to label filter) -FUZZING_ISSUES=$(echo "$EXISTING_ISSUES" | jq '.nodes[]' 2>/dev/null || echo "") -FUZZING_ISSUE_COUNT=0 -if [ -n "$FUZZING_ISSUES" ]; then - FUZZING_ISSUE_COUNT=$(echo "$FUZZING_ISSUES" | jq -s 'length' 2>/dev/null || echo "0") -fi - -echo "Found $FUZZING_ISSUE_COUNT existing fuzzing failure issue(s)" - -if [ "$FUZZING_ISSUE_COUNT" -gt 0 ]; then - # Get the most recent fuzzing failure issue - ISSUE_NUMBER=$(echo "$FUZZING_ISSUES" | jq -r '.number' | head -1) - ISSUE_URL=$(echo "$FUZZING_ISSUES" | jq -r '.url' | head -1) - - if [ "$ISSUE_NUMBER" = "null" ] || [ -z "$ISSUE_NUMBER" ]; then - echo "⚠️ Could not parse issue number from fuzzing issues, creating new issue instead" - FUZZING_ISSUE_COUNT=0 - else - echo "Adding comment to existing issue #$ISSUE_NUMBER" - - # Create comment body - COMMENT_BODY="## Fuzzing Job Failed Again - -**Date:** $(date '+%Y-%m-%d %H:%M:%S UTC') -**Workflow Run:** [$WORKFLOW_RUN_URL]($WORKFLOW_RUN_URL) -**Fuzzing Targets:** $FUZZING_TARGETS - -The scheduled fuzzing job has failed again. Please check the workflow logs and artifacts for details." - - # Add comment to the existing issue - if gh issue comment "$ISSUE_NUMBER" --body "$COMMENT_BODY" --repo "$REPO"; then - echo "✅ Added comment to existing issue #$ISSUE_NUMBER: $ISSUE_URL" - else - echo "❌ Failed to add comment to existing issue. Creating new issue instead." - FUZZING_ISSUE_COUNT=0 - fi - fi -fi - -if [ "$FUZZING_ISSUE_COUNT" -eq 0 ]; then - echo "No existing fuzzing failure issues found. Creating new issue..." - - # Create issue body - ISSUE_BODY="## Fuzzing Job Failure Report - -**Date:** $(date '+%Y-%m-%d %H:%M:%S UTC') -**Workflow Run:** [$WORKFLOW_RUN_URL]($WORKFLOW_RUN_URL) -**Fuzzing Targets:** $FUZZING_TARGETS - -The scheduled fuzzing job has failed. This issue was automatically created to track the failure. - -### Details -The fuzzing workflow failed during execution. Please check the workflow logs and any uploaded artifacts for more details. - -### Next Steps -- [ ] Review the workflow logs for error details -- [ ] Download and analyze any crash artifacts if available -- [ ] Determine the root cause of the failure -- [ ] Fix the underlying issue - -### Related Documentation -- [Fuzzing README](https://github.com/$REPO/blob/main/fuzz/README.md) -- [Security Guidance](https://github.com/$REPO/blob/main/docs/security-guidance-for-developers.md) - ---- -*This issue was automatically created by the fuzzing failure notification system.*" - - # Create the new issue - if ISSUE_URL=$(gh issue create \ - --title "$ISSUE_TITLE" \ - --body "$ISSUE_BODY" \ - --label "$TESTING_LABEL" \ - --label "$FAILURE_LABEL" \ - --label "$FUZZING_LABEL" \ - --label "$LIFECYCLE_LABEL" \ - --repo "$REPO"); then - echo "✅ Created new fuzzing failure issue: $ISSUE_URL" - else - echo "❌ Failed to create new fuzzing failure issue" - exit 1 - fi -fi - -echo "Fuzzing failure notification completed successfully" \ No newline at end of file From ff140e37dc24be8e0f8e4ae4abd90aca6462e359 Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Wed, 10 Sep 2025 23:23:28 +0000 Subject: [PATCH 6/9] Filter the location to a local path relative to the project this helps in ci when using the musl container which will end up with the wrong path Signed-off-by: James Sturtevant --- Justfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Justfile b/Justfile index e9b8b1bd6..5de609de5 100644 --- a/Justfile +++ b/Justfile @@ -209,6 +209,7 @@ test-rust-tracing target=default-target features="": # note that trace-dump doesn't run on MUSL target as of now TRACE_OUTPUT="$({{ cargo-cmd }} run --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} --example hello-world --features {{ if features =="" {"trace_guest"} else { "trace_guest," + features } }})" && \ TRACE_FILE="$(echo "$TRACE_OUTPUT" | grep -oE 'Creating trace file at: [^ ]+' | awk -F': ' '{print $2}')" && \ + TRACE_FILE="$(echo "$TRACE_OUTPUT" | grep -oE 'Creating trace file at: [^ ]+' | awk -F': ' '{print $2}' | sed -E 's|^(trace/[^ ]+\.trace)$|./\1|; s|.*/(trace/[^ ]+\.trace)$|./\1|')" && \ echo "$TRACE_OUTPUT" && \ if [ -z "$TRACE_FILE" ]; then \ echo "Error: Could not extract trace file path from output." >&2 ; \ From 443deaa7e4ebbfe908ed24193e2562d4e01cbc17 Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Thu, 11 Sep 2025 21:35:50 +0000 Subject: [PATCH 7/9] fixup! Refactor CI failure and add to nightly job Signed-off-by: James Sturtevant --- .github/workflows/RustNightly.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/RustNightly.yml b/.github/workflows/RustNightly.yml index 370c55225..fce399b31 100644 --- a/.github/workflows/RustNightly.yml +++ b/.github/workflows/RustNightly.yml @@ -5,6 +5,7 @@ name: Nightly on: workflow_dispatch: schedule: + # 12:00 AM, every 2 days - cron: '0 0 */2 * *' jobs: From 112bbdca32b8aafc3d7b5357b5a4acfcd62a670e Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Thu, 11 Sep 2025 21:37:54 +0000 Subject: [PATCH 8/9] fixup! Disable seccomp for musl target Signed-off-by: James Sturtevant --- src/hyperlight_host/src/sandbox/host_funcs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hyperlight_host/src/sandbox/host_funcs.rs b/src/hyperlight_host/src/sandbox/host_funcs.rs index 231630552..b61a2cd77 100644 --- a/src/hyperlight_host/src/sandbox/host_funcs.rs +++ b/src/hyperlight_host/src/sandbox/host_funcs.rs @@ -199,7 +199,7 @@ fn maybe_with_seccomp( }) } -#[cfg(not(all(seccomp)))] +#[cfg(not(seccomp))] fn maybe_with_seccomp( _name: &str, _syscalls: Option<&[ExtraAllowedSyscall]>, From bc87e54a366d27e5c2effef4061bfec9be4e50e2 Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Fri, 12 Sep 2025 16:56:05 +0000 Subject: [PATCH 9/9] Update devcontainer with tools for musl Signed-off-by: James Sturtevant --- .devcontainer/Dockerfile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ee79b5dff..a236a97c5 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -22,7 +22,8 @@ RUN apt-get update \ make \ software-properties-common \ sudo \ - wget + wget \ + musl-tools ARG LLVM_VERSION=18 @@ -54,7 +55,9 @@ ARG RUST_TOOLCHAIN=1.86 RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ && rustup default ${RUST_TOOLCHAIN} \ && rustup target add x86_64-unknown-linux-gnu \ + && rustup target add x86_64-unknown-linux-musl \ && rustup target add x86_64-unknown-none \ && rustup toolchain add nightly-x86_64-unknown-linux-gnu \ - && cargo install just - + && rustup toolchain add nightly-x86_64-unknown-linux-musl \ + && cargo install just \ + cargo install cross --locked --version 0.2.5