Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,56 @@ test-like-ci config=default-target hypervisor="kvm":
just test-rust-crashdump {{config}}

@# test the tracing related features
just test-rust-tracing {{config}} {{ if hypervisor == "mshv3" {"mshv3"} else {""} }}
{{ if os() == "linux" { "just test-rust-tracing " + config + " " + if hypervisor == "mshv" { "mshv2" } else if hypervisor == "mshv3" { "mshv3" } else { "kvm" } } else { "" } }}

like-ci config=default-target hypervisor="kvm":
@# Ensure up-to-date Cargo.lock
cargo fetch --locked

@# fmt
just fmt-check

@# clippy
{{ if os() == "windows" { "just clippy " + config } else { "" } }}
{{ if os() == "windows" { "just clippy-guests " + config } else { "" } }}

@# clippy exhaustive check
{{ if os() == "linux" { "just clippy-exhaustive " + config } else { "" } }}

@# Verify MSRV
./dev/verify-msrv.sh hyperlight-host hyperlight-guest hyperlight-guest-bin hyperlight-common

@# Build and move Rust guests
just build-rust-guests {{config}}
just move-rust-guests {{config}}

@# Build c guests
just build-c-guests {{config}}
just move-c-guests {{config}}

@# Build
just build {{config}}

@# Run Rust tests
just test-like-ci {{config}} {{hypervisor}}

@# Run Rust examples - Windows
{{ if os() == "windows" { "just run-rust-examples " + config } else { "" } }}

@# Run Rust examples - linux
{{ if os() == "linux" { "just run-rust-examples-linux " + config + " " + if hypervisor == "mshv" { "mshv2" } else if hypervisor == "mshv3" { "mshv3" } else { "kvm" } } else { "" } }}

@# Run Rust Gdb tests - linux
{{ if os() == "linux" { "just test-rust-gdb-debugging " + config + " " + if hypervisor == "mshv" { "mshv2" } else if hypervisor == "mshv3" { "mshv3" } else { "kvm" } } else { "" } }}

@# Run Rust Crashdump tests
just test-rust-crashdump {{config}} {{ if hypervisor == "mshv" { "mshv2" } else if hypervisor == "mshv3" { "mshv3" } else { "kvm" } }}

@# Run Rust Tracing tests - linux
{{ if os() == "linux" { "just test-rust-tracing " + config + " " + if hypervisor == "mshv" { "mshv2" } else if hypervisor == "mshv3" { "mshv3" } else { "kvm" } } else { "" } }}

@# Run benchmarks
just bench-ci main {{config}} {{ if hypervisor == "mshv" { "mshv2" } else if hypervisor == "mshv3" { "mshv3" } else { "kvm" } }}

# runs all tests
test target=default-target features="": (test-unit target features) (test-isolated target features) (test-integration "rust" target features) (test-integration "c" target features) (test-seccomp target features)
Expand Down
2 changes: 1 addition & 1 deletion dev/verify-msrv.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -Eeuo pipefail
cargo install -q jaq
for CRATE in "$@"; do
Expand Down
44 changes: 44 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

145 changes: 145 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
inputs.nixpkgs-mozilla.url = "github:mozilla/nixpkgs-mozilla/master";
outputs = { self, nixpkgs, nixpkgs-mozilla, ... } @ inputs:
{
devShells.x86_64-linux.default =
let pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ (import (nixpkgs-mozilla + "/rust-overlay.nix")) ];
};
in with pkgs; let
# Work around the nixpkgs-mozilla equivalent of
# https://github.com/NixOS/nixpkgs/issues/278508 and an
# incompatibility between nixpkgs-mozilla and makeRustPlatform
rustChannelOf = args: let
orig = pkgs.rustChannelOf args;
patchRustPkg = pkg: (pkg.overrideAttrs (oA: {
buildCommand = builtins.replaceStrings
[ "rustc,rustdoc" ]
[ "rustc,rustdoc,clippy-driver,cargo-clippy" ]
oA.buildCommand;
})) // {
targetPlatforms = [ "x86_64-linux" ];
badTargetPlatforms = [ ];
};
overrideRustPkg = pkg: lib.makeOverridable (origArgs:
patchRustPkg (pkg.override origArgs)
) {};
in builtins.mapAttrs (_: overrideRustPkg) orig;

customisedRustChannelOf = args:
lib.flip builtins.mapAttrs (rustChannelOf args) (_: pkg: pkg.override {
targets = [
"x86_64-unknown-linux-gnu"
"x86_64-pc-windows-msvc" "x86_64-unknown-none"
"wasm32-wasip1" "wasm32-wasip2" "wasm32-unknown-unknown"
];
extensions = [ "rust-src" ];
});

# Hyperlight needs a variety of toolchains, since we use Nightly
# for rustfmt and old toolchains to verify MSRV
toolchains = lib.mapAttrs (_: customisedRustChannelOf) {
stable = {
# Stay on 1.87 for development due to the
# quickly-reversed default enablement of
# #[warn(clippy::uninlined_format_args)]
date = "2025-05-15";
channel = "stable";
sha256 = "sha256-KUm16pHj+cRedf8vxs/Hd2YWxpOrWZ7UOrwhILdSJBU=";
};
nightly = {
date = "2025-07-29";
channel = "nightly";
sha256 = "sha256-6D2b7glWC3jpbIGCq6Ta59lGCKN9sTexhgixH4Y7Nng=";
};
"1.85" = {
date = "2025-02-20";
channel = "stable";
sha256 = "sha256-AJ6LX/Q/Er9kS15bn9iflkUwcgYqRQxiOIL2ToVAXaU=";
};
};

rust-platform = makeRustPlatform {
cargo = toolchains.stable.rust;
rustc = toolchains.stable.rust;
};

# Hyperlight scripts use cargo in a bunch of ways that don't
# make sense for Nix cargo, including the `rustup +toolchain`
# syntax to use a specific toolchain and `cargo install`, so we
# build wrappers for rustc and cargo that enable this. The
# scripts also use `rustup toolchain install` in some cases, in
# order to work in CI, so we provide a fake rustup that does
# nothing as well.
rustup-like-wrapper = name: pkgs.writeShellScriptBin name
(let
clause = name: toolchain:
"+${name}) base=\"${toolchain.rust}\"; shift 1; ;;";
clauses = lib.strings.concatStringsSep "\n"
(lib.mapAttrsToList clause toolchains);
in ''
base="${toolchains.stable.rust}"
case "$1" in
${clauses}
install) exit 0; ;;
esac
export PATH="$base/bin:$PATH"
exec "$base/bin/${name}" "$@"
'');
fake-rustup = pkgs.symlinkJoin {
name = "fake-rustup";
paths = [
(pkgs.writeShellScriptBin "rustup" "")
(rustup-like-wrapper "rustc")
(rustup-like-wrapper "cargo")
];
};

buildRustPackageClang = rust-platform.buildRustPackage.override { stdenv = clangStdenv; };
in (buildRustPackageClang rec {
pname = "hyperlight";
version = "0.0.0";
src = lib.cleanSource ./.;
cargoHash = "sha256-mNKnsaSKVz4khzWO7VhmN0cR+Ed5ML7fD1PJJCeQQ6E=";

nativeBuildInputs = [
azure-cli
just
dotnet-sdk_9
llvmPackages_18.llvm
gh
lld
valgrind
pkg-config
ffmpeg
mkvtoolnix
wasm-tools
jq
jaq
gdb
];
buildInputs = [
pango
cairo
openssl
];

auditable = false;

LIBCLANG_PATH = "${pkgs.llvmPackages_18.libclang.lib}/lib";
# Use unwrapped clang for compiling guests
HYPERLIGHT_GUEST_clang = "${clang.cc}/bin/clang";

RUST_NIGHTLY = "${toolchains.nightly.rust}";
# Set this through shellHook rather than nativeBuildInputs to be
# really sure that it overrides the real cargo.
shellHook = ''
export PATH="${fake-rustup}/bin:$PATH"
'';
}).overrideAttrs(oA: {
hardeningDisable = [ "all" ];
});
};
}
7 changes: 5 additions & 2 deletions src/hyperlight_component_util/src/subtype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub enum Error<'r> {
/// A value type was present, but incompatible with its expected type
MismatchedValue(Value<'r>, Value<'r>),
/// A defined type was present, but incompatible with its expected type
MismatchedDefined(Defined<'r>, Defined<'r>),
MismatchedDefined(Box<Defined<'r>>, Box<Defined<'r>>),
/// A resource was present, but was not the same resource as was expected
MismatchedResources(ResourceId, ResourceId),
/// A type variable could not be resolved to be the same as the
Expand Down Expand Up @@ -239,7 +239,10 @@ impl<'p, 'a> Ctx<'p, 'a> {
self.subtype_qualified_instance(it1, it2)
}
(Defined::Component(ct1), Defined::Component(ct2)) => self.subtype_component(ct1, ct2),
_ => Err(Error::MismatchedDefined(dt1.clone(), dt2.clone())),
_ => Err(Error::MismatchedDefined(
Box::new(dt1.clone()),
Box::new(dt2.clone()),
)),
}
}
pub fn subtype_handleable_is_resource<'r>(&self, ht: &'r Handleable) -> Result<(), Error<'a>> {
Expand Down
2 changes: 1 addition & 1 deletion src/hyperlight_guest_bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ and third-party code used by our C-API needed to build a native hyperlight-guest
[features]
default = ["libc", "printf"]
libc = [] # compile musl libc
printf = [] # compile printf
printf = [ "libc" ] # compile printf
trace_guest = ["hyperlight-common/trace_guest", "dep:hyperlight-guest-tracing", "hyperlight-guest/trace_guest"]
mem_profile = ["hyperlight-common/unwind_guest","hyperlight-common/mem_profile"]

Expand Down
11 changes: 9 additions & 2 deletions src/hyperlight_guest_bin/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ fn cargo_main() {
cfg.flag("-fno-stack-protector");
cfg.flag("-fstack-clash-protection");
cfg.flag("-mstack-probe-size=4096");
cfg.compiler("clang");
cfg.compiler(
env::var("HYPERLIGHT_GUEST_clang")
.as_deref()
.unwrap_or("clang"),
);

if cfg!(windows) {
unsafe { env::set_var("AR_x86_64_unknown_none", "llvm-ar") };
Expand Down Expand Up @@ -197,7 +201,10 @@ impl From<&std::ffi::OsStr> for Tool {
}

fn find_next(root_dir: &Path, tool_name: &str) -> PathBuf {
let path = env::var_os("PATH").expect("$PATH should exist");
if let Some(path) = env::var_os(format!("HYPERLIGHT_GUEST_{tool_name}")) {
return path.into();
}
let path = env::var_os("PATH)").expect("$PATH should exist");
let paths: Vec<_> = env::split_paths(&path).collect();
for path in &paths {
let abs_path = fs::canonicalize(path);
Expand Down
14 changes: 7 additions & 7 deletions src/hyperlight_host/src/hypervisor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,8 +373,8 @@ pub trait InterruptHandle: Debug + Send + Sync {
///
/// - If this is called while the vcpu is running, then it will interrupt the vcpu and return `true`.
/// - If this is called while the vcpu is not running, (for example during a host call), the
/// vcpu will not immediately be interrupted, but will prevent the vcpu from running **the next time**
/// it's scheduled, and returns `false`.
/// vcpu will not immediately be interrupted, but will prevent the vcpu from running **the next time**
/// it's scheduled, and returns `false`.
///
/// # Note
/// This function will block for the duration of the time it takes for the vcpu thread to be interrupted.
Expand All @@ -384,8 +384,8 @@ pub trait InterruptHandle: Debug + Send + Sync {
///
/// - If this is called while the vcpu is running, then it will interrupt the vcpu and return `true`.
/// - If this is called while the vcpu is not running, (for example during a host call), the
/// vcpu will not immediately be interrupted, but will prevent the vcpu from running **the next time**
/// it's scheduled, and returns `false`.
/// vcpu will not immediately be interrupted, but will prevent the vcpu from running **the next time**
/// it's scheduled, and returns `false`.
///
/// # Note
/// This function will block for the duration of the time it takes for the vcpu thread to be interrupted.
Expand All @@ -407,7 +407,7 @@ pub(super) struct LinuxInterruptHandle {
/// 1. The VCPU is running (generation N),
/// 2. It gets cancelled,
/// 3. Then quickly restarted (generation N+1),
/// before the original thread has observed that it was cancelled.
/// before the original thread has observed that it was cancelled.
///
/// Without this generation counter, the interrupt logic might assume the VCPU is still
/// in the *original* run (generation N), see that it's `running`, and re-send the signal.
Expand All @@ -423,9 +423,9 @@ pub(super) struct LinuxInterruptHandle {
/// `kill()` is called, and cleared when the vcpu is no longer running.
/// This is used to
/// 1. make sure stale signals do not interrupt the
/// the wrong vcpu (a vcpu may only be interrupted iff `cancel_requested` is true),
/// the wrong vcpu (a vcpu may only be interrupted iff `cancel_requested` is true),
/// 2. ensure that if a vm is killed while a host call is running,
/// the vm will not re-enter the guest after the host call returns.
/// the vm will not re-enter the guest after the host call returns.
cancel_requested: AtomicBool,
/// True when the debugger has requested the VM to be interrupted. Set immediately when
/// `kill_from_debugger()` is called, and cleared when the vcpu is no longer running.
Expand Down
2 changes: 1 addition & 1 deletion src/hyperlight_host/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub mod hypervisor;
/// present and code length will be zero;
///
/// - The pointer passed to the Entrypoint in the Guest application is the size of page table + size of code,
/// at this address structs below are laid out in this order
/// at this address structs below are laid out in this order
pub mod mem;
/// Metric definitions and helpers
pub mod metrics;
Expand Down
2 changes: 1 addition & 1 deletion src/hyperlight_host/src/mem/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ use crate::{Result, new_error};
// +-------------------------------------------+ 0x0_000

/// - `InitData` - some extra data that can be loaded onto the sandbox during
/// initialization.
/// initialization.
///
/// - `HostDefinitions` - the length of this is the `HostFunctionDefinitionSize`
/// field from `SandboxConfiguration`
Expand Down
5 changes: 2 additions & 3 deletions src/hyperlight_host/src/mem/shared_mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,7 +785,7 @@ impl HostSharedMemory {
/// patterns
pub fn read<T: AllValid>(&self, offset: usize) -> Result<T> {
bounds_check!(offset, std::mem::size_of::<T>(), self.mem_size());
let ret = unsafe {
unsafe {
let mut ret: core::mem::MaybeUninit<T> = core::mem::MaybeUninit::uninit();
{
let slice: &mut [u8] = core::slice::from_raw_parts_mut(
Expand All @@ -795,8 +795,7 @@ impl HostSharedMemory {
self.copy_to_slice(slice, offset)?;
}
Ok(ret.assume_init())
};
ret
}
}

/// Write a value of type T, whose representation is the same
Expand Down
Loading