Skip to content
Open
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
5 changes: 5 additions & 0 deletions .github/workflows/dep_build_guests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ jobs:
just build-rust-guests ${{ inputs.config }}
just move-rust-guests ${{ inputs.config }}

- name: Build non-PIE Rust guests
run: |
just build-rust-guests-non-pie ${{ inputs.config }}
just move-rust-guests-non-pie ${{ inputs.config }}

- name: Build C guests
run: |
just build-c-guests ${{ inputs.config }}
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ $RECYCLE.BIN/

# Rust build artifacts
**/**target
**/**target-non-pie
libhyperlight_host.so
libhyperlight_host.d
hyperlight_host.dll
Expand Down
18 changes: 17 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ build target=default-target:
{{ 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
guests: build-and-move-rust-guests build-and-move-rust-guests-non-pie build-and-move-c-guests

# Ensure the pinned cargo-hyperlight is installed. We compare the *actual*
# installed binary's reported version instead of relying on `cargo install`
Expand All @@ -75,6 +75,22 @@ build-rust-guests target=default-target features="": (ensure-cargo-hyperlight)
build-and-move-rust-guests: (build-rust-guests "debug") (move-rust-guests "debug") (build-rust-guests "release") (move-rust-guests "release")
build-and-move-c-guests: (build-c-guests "debug") (move-c-guests "debug") (build-c-guests "release") (move-c-guests "release")

# Build non-PIE variants of rust guests for testing ELF VA mapping.
# Phase 1 builds the sysroot without RUSTFLAGS (avoids RUSTFLAGS leaking
# into the sysroot wrapper build in cargo-hyperlight).
# Phase 2 uses plain cargo with --sysroot and non-PIE link flags.
build-rust-guests-non-pie target=default-target: (ensure-cargo-hyperlight)
cd src/tests/rust_guests/simpleguest && cargo hyperlight build --target-dir ../target-non-pie --profile={{ if target == "debug" { "dev" } else { target } }}
{{ if os() == "windows" { "$env:RUSTC_BOOTSTRAP=1; $env:RUSTFLAGS='--sysroot=' + (Resolve-Path src/tests/rust_guests/target-non-pie/sysroot).Path + ' -C relocation-model=static -C link-args=--no-pie -C link-args=--image-base=0x1000000 --cfg=hyperlight --check-cfg=cfg(hyperlight) -Clink-args=-eentrypoint';" } else { "" } }} cd src/tests/rust_guests/simpleguest && {{ if os() == "windows" { "" } else { "RUSTC_BOOTSTRAP=1 RUSTFLAGS=\"--sysroot=$(cd .. && pwd)/target-non-pie/sysroot -C relocation-model=static -C link-args=--no-pie -C link-args=--image-base=0x1000000 --cfg=hyperlight --check-cfg=cfg(hyperlight) -Clink-args=-eentrypoint\"" } }} cargo build --target x86_64-hyperlight-none --target-dir ../target-non-pie/build --profile={{ if target == "debug" { "dev" } else { target } }}

non_pie_guests_target := "src/tests/rust_guests/target-non-pie/build/x86_64-hyperlight-none"

@move-rust-guests-non-pie target=default-target:
{{ if os() == "windows" { "New-Item -ItemType Directory -Path " + rust_guests_bin_dir + "/" + target + "/non_pie -Force | Out-Null" } else { "mkdir -p " + rust_guests_bin_dir + "/" + target + "/non_pie" } }}
cp {{ non_pie_guests_target }}/{{ target }}/simpleguest {{ rust_guests_bin_dir }}/{{ target }}/non_pie/

build-and-move-rust-guests-non-pie: (build-rust-guests-non-pie "debug") (move-rust-guests-non-pie "debug") (build-rust-guests-non-pie "release") (move-rust-guests-non-pie "release")

clean: clean-rust

clean-rust:
Expand Down
1 change: 1 addition & 0 deletions src/hyperlight_host/src/hypervisor/crashdump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ mod test {
let ptr = dummy_vec.as_ptr() as usize;
let regions = vec![CrashDumpRegion {
guest_region: 0x1000..0x2000,
guest_virt_addr: 0x1000,
host_region: ptr..ptr + dummy_vec.len(),
flags: MemoryRegionFlags::READ | MemoryRegionFlags::WRITE,
region_type: crate::mem::memory_region::MemoryRegionType::Code,
Expand Down
1 change: 1 addition & 0 deletions src/hyperlight_host/src/hypervisor/gdb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ mod tests {
guest_mmap_regions: vec![MemoryRegion {
host_region: mapped_mem as usize..mapped_mem.wrapping_add(size) as usize,
guest_region: BASE_VIRT..BASE_VIRT + size,
guest_virt_addr: BASE_VIRT,
flags: MemoryRegionFlags::READ | MemoryRegionFlags::EXECUTE,
region_type: MemoryRegionType::Heap,
}],
Expand Down
9 changes: 9 additions & 0 deletions src/hyperlight_host/src/mem/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
#[cfg(feature = "mem_profile")]
use std::sync::Arc;

use goblin::elf::header::ET_DYN;
#[cfg(target_arch = "aarch64")]
use goblin::elf::reloc::{R_AARCH64_NONE, R_AARCH64_RELATIVE};
#[cfg(target_arch = "x86_64")]
Expand All @@ -42,6 +43,8 @@ pub(crate) struct ElfInfo {
shdrs: Vec<ResolvedSectionHeader>,
entry: u64,
relocs: Vec<Reloc>,
/// Whether this is a position-independent executable (ET_DYN).
is_pie: bool,
/// The hyperlight version string embedded by `hyperlight-guest-bin`, if
/// present. Used to detect version/ABI mismatches between guest and host.
guest_bin_version: Option<String>,
Expand Down Expand Up @@ -143,6 +146,7 @@ impl ElfInfo {
.collect(),
entry: elf.entry,
relocs,
is_pie: elf.header.e_type == ET_DYN,
guest_bin_version,
})
}
Expand All @@ -168,6 +172,11 @@ impl ElfInfo {
self.entry
}

/// Returns whether this is a position-independent executable (ET_DYN).
pub(crate) fn is_pie(&self) -> bool {
self.is_pie
}

/// Returns the hyperlight version string embedded in the guest binary, if
/// present. Used to detect version/ABI mismatches between guest and host.
pub(crate) fn guest_bin_version(&self) -> Option<&str> {
Expand Down
6 changes: 6 additions & 0 deletions src/hyperlight_host/src/mem/exe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ impl ExeInfo {
ExeInfo::Elf(elf) => Offset::from(elf.entrypoint_va()),
}
}
/// Returns whether this is a position-independent executable (ET_DYN).
pub fn is_pie(&self) -> bool {
match self {
ExeInfo::Elf(elf) => elf.is_pie(),
}
}
/// Returns the base virtual address of the loaded binary (lowest PT_LOAD p_vaddr).
pub fn base_va(&self) -> u64 {
match self {
Expand Down
64 changes: 61 additions & 3 deletions src/hyperlight_host/src/mem/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ use std::mem::size_of;
use hyperlight_common::mem::{HyperlightPEB, PAGE_SIZE_USIZE};
use tracing::{Span, instrument};

use super::memory_region::MemoryRegionType::{Code, Heap, InitData, Peb};
use super::memory_region::MemoryRegionType::{self, Code, Heap, InitData, Peb};
use super::memory_region::{
DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegion, MemoryRegion_, MemoryRegionFlags, MemoryRegionKind,
MemoryRegionVecBuilder,
DEFAULT_GUEST_BLOB_MEM_FLAGS, GuestMemoryRegion, MemoryRegion, MemoryRegion_,
MemoryRegionFlags, MemoryRegionKind, MemoryRegionVecBuilder,
};
#[cfg(readable_shared_mem)]
use super::shared_mem::HostSharedMemory;
Expand Down Expand Up @@ -555,6 +555,64 @@ impl SandboxMemoryLayout {
Ok(builder.build())
}

/// Compute the virtual base address for the code region, validate
/// that it does not overlap any other memory region, and return the
/// guest memory regions with the Code region's `guest_virt_addr`
/// already set to the computed virtual base.
///
/// For PIE binaries (`is_pie == true`), the code is identity-mapped so
/// the virtual base equals the physical load address and no conflict
/// is possible by construction.
///
/// For non-PIE binaries, the code appears at the ELF's declared
/// virtual address (`elf_base_va`), which may differ from the physical
/// load address. This method checks that the resulting virtual range
/// `[elf_base_va, elf_base_va + loaded_size)` does not overlap any
/// non-Code region.
///
/// Returns `(code_virt_base, regions)`.
pub(crate) fn get_guest_regions_with_code_va(
&self,
is_pie: bool,
elf_base_va: u64,
loaded_size: u64,
) -> Result<(u64, Vec<MemoryRegion_<GuestMemoryRegion>>)> {
let load_addr = self.get_guest_code_address() as u64;
let code_virt_base = if is_pie { load_addr } else { elf_base_va };

let mut regions = self.get_memory_regions_::<GuestMemoryRegion>(())?;

if !is_pie {
let code_virt_end = code_virt_base + loaded_size;
for rgn in regions.iter() {
if rgn.region_type == MemoryRegionType::Code {
continue;
}
let rgn_start = rgn.guest_region.start as u64;
let rgn_end = rgn_start + rgn.guest_region.len() as u64;
if code_virt_base < rgn_end && rgn_start < code_virt_end {
return Err(new_error!(
"Non-PIE code mapping [{:#x}, {:#x}) conflicts with {:?} region [{:#x}, {:#x})",
code_virt_base,
code_virt_end,
rgn.region_type,
rgn_start,
rgn_end,
));
}
}
}

// Set the Code region's guest_virt_addr to code_virt_base.
for rgn in regions.iter_mut() {
if rgn.region_type == MemoryRegionType::Code {
rgn.guest_virt_addr = code_virt_base as usize;
}
}

Ok((code_virt_base, regions))
}

#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
pub(crate) fn write_init_data(&self, out: &mut [u8], bytes: &[u8]) -> Result<()> {
out[self.init_data_offset()..self.init_data_offset() + self.init_data_size]
Expand Down
11 changes: 9 additions & 2 deletions src/hyperlight_host/src/mem/memory_region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,12 @@ impl MemoryRegionKind for GuestMemoryRegion {
/// the same memory permissions
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MemoryRegion_<K: MemoryRegionKind> {
/// the range of guest memory addresses
/// the range of guest physical addresses
pub guest_region: Range<usize>,
/// the guest virtual address at which this region should be mapped.
/// For identity-mapped regions this equals `guest_region.start`.
/// For non-PIE code it is the ELF's declared virtual address.
pub guest_virt_addr: usize,
/// the range of host memory addresses
///
/// Note that Range<()> = () x () = ().
Expand Down Expand Up @@ -356,6 +360,7 @@ impl<K: MemoryRegionKind> MemoryRegionVecBuilder<K> {
let host_end = <K as MemoryRegionKind>::add(self.host_base_virt_addr, size);
self.regions.push(MemoryRegion_ {
guest_region: self.guest_base_phys_addr..guest_end,
guest_virt_addr: self.guest_base_phys_addr,
host_region: self.host_base_virt_addr..host_end,
flags,
region_type,
Expand All @@ -367,8 +372,10 @@ impl<K: MemoryRegionKind> MemoryRegionVecBuilder<K> {
// we know this is safe because we check if the regions are empty above
let last_region = self.regions.last().unwrap();
let host_end = <K as MemoryRegionKind>::add(last_region.host_region.end, size);
let guest_start = last_region.guest_region.end;
let new_region = MemoryRegion_ {
guest_region: last_region.guest_region.end..last_region.guest_region.end + size,
guest_region: guest_start..guest_start + size,
guest_virt_addr: guest_start,
host_region: last_region.host_region.end..host_end,
flags,
region_type,
Expand Down
11 changes: 11 additions & 0 deletions src/hyperlight_host/src/mem/mgr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ pub(crate) struct SandboxMemoryManager<S: SharedMemory> {
/// preserved across the `Initialise` -> `Call` transition so it
/// can fill `AT_ENTRY` in guest core dumps. 0 if unknown.
pub(crate) original_entrypoint: u64,
/// Virtual base address of the code region.
/// For PIE binaries this equals the physical load address (identity-mapped).
/// For non-PIE binaries this is the ELF-declared base VA.
pub(crate) code_virt_base: u64,
/// Buffer for accumulating guest abort messages
pub(crate) abort_buffer: Vec<u8>,
/// Generation counter: how many snapshots have been taken from
Expand Down Expand Up @@ -288,6 +292,7 @@ where
scratch_mem,
next_action,
original_entrypoint: 0,
code_virt_base: 0,
abort_buffer: Vec::new(),
snapshot_count: 0,
}
Expand Down Expand Up @@ -320,6 +325,7 @@ where
rsp_gva,
sregs,
next_action,
self.code_virt_base,
self.original_entrypoint,
self.snapshot_count,
host_functions,
Expand All @@ -335,6 +341,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
let next_action = s.next_action();
let mut mgr = Self::new(layout, shared_mem, scratch_mem, next_action);
mgr.original_entrypoint = s.original_entrypoint();
mgr.code_virt_base = s.code_virt_base;
// Inherit the snapshot's generation number for the same
// reason `restore_snapshot` does: the guest-visible counter
// reflects "which snapshot is the sandbox currently a clone
Expand Down Expand Up @@ -367,6 +374,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
layout: self.layout,
next_action: self.next_action,
original_entrypoint: self.original_entrypoint,
code_virt_base: self.code_virt_base,
abort_buffer: self.abort_buffer,
snapshot_count: self.snapshot_count,
};
Expand All @@ -376,6 +384,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
layout: self.layout,
next_action: self.next_action,
original_entrypoint: self.original_entrypoint,
code_virt_base: self.code_virt_base,
abort_buffer: Vec::new(), // Guest doesn't need abort buffer
snapshot_count: self.snapshot_count,
};
Expand Down Expand Up @@ -516,6 +525,7 @@ impl SandboxMemoryManager<HostSharedMemory> {
// Carry the guest ELF entry point across restore so crashdumps
// report the restored image's entry.
self.original_entrypoint = snapshot.original_entrypoint();
self.code_virt_base = snapshot.code_virt_base;

self.update_scratch_bookkeeping()?;
Ok((gsnapshot, gscratch))
Expand Down Expand Up @@ -637,6 +647,7 @@ impl SandboxMemoryManager<HostSharedMemory> {

regions.push(CrashDumpRegion {
guest_region: virt_base..virt_end,
guest_virt_addr: virt_base,
host_region: host_base..host_base + host_len,
flags,
region_type,
Expand Down
1 change: 1 addition & 0 deletions src/hyperlight_host/src/mem/shared_mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ fn mapping_at(

MemoryRegion {
guest_region: guest_base..(guest_base + size),
guest_virt_addr: guest_base,
host_region: s.host_region_base()
..<HostGuestMemoryRegion as MemoryRegionKind>::add(s.host_region_base(), size),
region_type,
Expand Down
2 changes: 2 additions & 0 deletions src/hyperlight_host/src/sandbox/file_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ impl PreparedFileMapping {
Ok(MemoryRegion {
host_region: host_base..host_end,
guest_region: guest_start..guest_end,
guest_virt_addr: guest_start,
flags: MemoryRegionFlags::READ | MemoryRegionFlags::EXECUTE,
region_type: MemoryRegionType::MappedFile,
})
Expand All @@ -184,6 +185,7 @@ impl PreparedFileMapping {
host_region: *mmap_base as usize
..(*mmap_base as usize).wrapping_add(*mmap_size),
guest_region: guest_start..guest_end,
guest_virt_addr: guest_start,
flags: MemoryRegionFlags::READ | MemoryRegionFlags::EXECUTE,
region_type: MemoryRegionType::MappedFile,
})
Expand Down
1 change: 1 addition & 0 deletions src/hyperlight_host/src/sandbox/initialized_multi_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1553,6 +1553,7 @@ mod tests {
MemoryRegion {
host_region: mem.host_region_base()..mem.host_region_end(),
guest_region: guest_base..(guest_base + len),
guest_virt_addr: guest_base,
flags,
region_type: MemoryRegionType::Heap,
}
Expand Down
1 change: 1 addition & 0 deletions src/hyperlight_host/src/sandbox/snapshot/file/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,7 @@ impl Snapshot {
sregs: Some(cfg.sregs),
next_action,
original_entrypoint: cfg.original_entrypoint_addr,
code_virt_base: 0, // TODO: persist code_virt_base in snapshot file format
snapshot_generation,
host_functions,
})
Expand Down
Loading
Loading