Skip to content

feat: enable ASLR for PIE guest binaries#1655

Open
cshung wants to merge 2 commits into
hyperlight-dev:mainfrom
cshung:cshung/aslr-pie-guests
Open

feat: enable ASLR for PIE guest binaries#1655
cshung wants to merge 2 commits into
hyperlight-dev:mainfrom
cshung:cshung/aslr-pie-guests

Conversation

@cshung

@cshung cshung commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Randomize the virtual base address for PIE guest code regions instead of using identity mapping. This provides address space layout randomization (ASLR) for PIE guests, making the code region virtual address unpredictable across sandbox instantiations.

Changes

  • layout.rs: \code_virt_base()\ now randomizes VA for PIE guests using
    and::rng(), choosing a page-aligned address within 47-bit canonical user space. Conflict validation now applies to both PIE and non-PIE.
  • mgr.rs: Thread \code_virt_base\ through \SandboxMemoryManager\ so it is preserved across snapshot/restore.
  • snapshot/mod.rs: Store \code_virt_base\ in \Snapshot, pass it to \�xe_info.load()\ for correct relocation processing, and expose via \code_virt_base()\ accessor.
  • config.rs: Relax entrypoint validation to accept non-identity-mapped virtual addresses (required for ASLR and non-PIE).
  • initialized_multi_use.rs: \ race_guest\ tests now use \code_virt_base\ instead of assuming GVA == GPA.

Design

  • The random base is chosen from [0x1000000, 0x7FFFFFFFFF * PAGE_SIZE - code_size), ensuring it is above all identity-mapped layout regions and within 47-bit canonical user space.
  • Non-PIE binaries continue to use their declared ELF base VA (no change).
  • Conflict validation ensures the randomized code region does not overlap other memory regions.

Testing

  • All unit tests pass (300 passed, 6 ignored)
  • All integration tests pass (39 passed, 1 ignored)
  • Clippy clean (debug + release)

Dependencies

This PR is based on #1530 (non-PIE ELF loading support).

Add support for running non-PIE (ET_EXEC) guest binaries by mapping
code at the ELF's declared virtual address rather than assuming
identity mapping (physical == virtual).

Changes:
- Add is_pie() and base_va() methods to ExeInfo/ElfInfo to detect
  ET_DYN vs ET_EXEC binaries and extract the base virtual address
- Add SandboxMemoryLayout::code_virt_base() to compute the correct
  virtual base for the code region and validate it doesn't conflict
  with other memory regions
- Update snapshot creation to use non-identity virtual mapping for
  non-PIE code regions
- Add non-PIE guest build step to CI (cargo hyperlight with
  -C relocation-model=static -C link-args=--no-pie)
- Add integration test verifying non-PIE guest execution
- Add test helper for locating non-PIE guest binaries

Signed-off-by: cshung <3410332+cshung@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6f20a05d-6bee-4e2e-b320-12f8d9759bbc
Signed-off-by: cshung <3410332+cshung@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 18, 2026 17:22
@cshung cshung added the kind/enhancement For PRs adding features, improving functionality, docs, tests, etc. label Jul 18, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces ASLR for PIE guest ELF binaries by randomizing the guest virtual base address of the code region, while preserving correct behavior for non-PIE binaries that must load at their declared ELF virtual addresses. It threads the chosen code_virt_base through snapshot creation/restore paths and updates tests and build infrastructure to cover non-PIE guests.

Changes:

  • Randomize PIE guest code virtual base and validate it against other guest mappings.
  • Preserve code_virt_base across snapshot/restore and use it for code-region GVA mappings and entrypoint computation.
  • Add non-PIE guest build targets and an integration test exercising non-PIE ELF VA mapping.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/hyperlight_testing/src/lib.rs Adds helper to locate the non-PIE simpleguest binary.
src/hyperlight_host/tests/integration_test.rs Adds an integration test that runs a non-PIE guest end-to-end.
src/hyperlight_host/src/sandbox/snapshot/mod.rs Computes/stores code_virt_base, uses it for loading, mapping, and entrypoint calculation.
src/hyperlight_host/src/sandbox/snapshot/file/mod.rs Wires new Snapshot field for file-loaded snapshots (currently placeholder value).
src/hyperlight_host/src/sandbox/snapshot/file/config.rs Relaxes entrypoint validation to allow non-identity-mapped code GVAs.
src/hyperlight_host/src/sandbox/initialized_multi_use.rs Updates trace-guest GVA tests to use code_virt_base.
src/hyperlight_host/src/mem/mgr.rs Threads code_virt_base through the memory manager so it survives restore/evolve flows.
src/hyperlight_host/src/mem/layout.rs Implements PIE code VA randomization and conflict validation for code mapping.
src/hyperlight_host/src/mem/exe.rs Exposes ExeInfo::is_pie() for ELF type classification.
src/hyperlight_host/src/mem/elf.rs Tracks PIE-ness (ET_DYN) in ELF metadata.
src/hyperlight_host/src/hypervisor/hyperlight_vm/x86_64.rs Updates internal tests for the expanded memory manager constructor.
Justfile Adds recipes to build and stage non-PIE guest binaries.
.gitignore Ignores the new non-PIE guest build target directory.
.github/workflows/dep_build_guests.yml Builds and stages non-PIE guests in CI guest-build workflow.

Comment thread src/hyperlight_host/src/mem/layout.rs Outdated
Comment on lines +561 to +569
/// 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.
Comment on lines +585 to +600
let min_page = 0x1000_u64; // 0x1000 * PAGE_SIZE = 0x1000000
let max_page = 0x7_FFFF_FFFF_u64 - code_size_pages;
let page_number = rng.random_range(min_page..max_page);
page_number * PAGE_SIZE_USIZE as u64
};

// Verify the code mapping does not conflict with other mappings
// (both non-PIE with declared VA and PIE with randomized ASLR base).
let code_virt_end = virt_base + loaded_size;
for rgn in self.get_memory_regions_::<GuestMemoryRegion>(())?.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 virt_base < rgn_end && rgn_start < code_virt_end {
Comment on lines 413 to 416
sregs: None,
entrypoint: NextAction::Initialise(load_addr + entrypoint_va - base_va),
code_virt_base,
entrypoint: NextAction::Initialise(code_virt_base + entrypoint_offset),
snapshot_generation: 0,
Comment on lines +459 to +463
// Entrypoint address must point inside the guest snapshot
// region `[BASE_ADDRESS, BASE_ADDRESS + snapshot_size)`. The
// address is a GVA, bounded by the same range because guests
// identity-map the snapshot region at low VAs. A guest
// dispatching from a non-identity-mapped VA must relax this
// check.
let snap_lo = SandboxMemoryLayout::BASE_ADDRESS as u64;
let snap_hi = snap_lo
.checked_add(self.layout.snapshot_size as u64)
.ok_or_else(|| {
crate::new_error!(
"snapshot layout overflow: BASE_ADDRESS + snapshot_size ({}) does not fit in u64",
self.layout.snapshot_size
)
})?;
if self.entrypoint_addr < snap_lo || self.entrypoint_addr >= snap_hi {
// `entrypoint_addr` is a GVA that will be loaded into RIP. For
// identity-mapped guests it falls inside the snapshot region
// `[BASE_ADDRESS, BASE_ADDRESS + snapshot_size)`. For ASLR or
// non-PIE guests the code may be mapped at a non-identity VA,
Comment on lines +874 to +876
// Deserialized snapshots don't record the original code_virt_base;
// use 0 as a placeholder (trace_guest tests won't rely on it here).
code_virt_base: 0,
Comment thread Justfile
Comment on lines +82 to +86
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"
Comment on lines +90 to +94
- name: Build non-PIE Rust guests
run: |
just build-rust-guests-non-pie ${{ inputs.config }}
just move-rust-guests-non-pie ${{ inputs.config }}

@cshung
cshung force-pushed the cshung/aslr-pie-guests branch 3 times, most recently from cb0b212 to 09e0700 Compare July 18, 2026 18:19
Randomize the virtual base address for PIE guest code regions instead
of using identity mapping. This provides address space layout
randomization (ASLR) for PIE guests, making the code region virtual
address unpredictable across sandbox instantiations.

The random base is chosen from a page-aligned range within 47-bit
canonical user space [0x1000000, max - code_size). Non-PIE binaries
continue to use their declared ELF base VA.

Changes:
- layout.rs: code_virt_base() now randomizes VA for PIE guests and
  always validates against memory region conflicts
- mgr.rs: thread code_virt_base through SandboxMemoryManager
- snapshot/mod.rs: store code_virt_base in Snapshot, use it for
  relocation processing in exe_info.load()
- config.rs: relax entrypoint validation to allow non-identity-mapped
  virtual addresses (ASLR / non-PIE)
- initialized_multi_use.rs: trace_guest tests use code_virt_base
  instead of assuming GVA == GPA

Signed-off-by: cshung <3410332+cshung@users.noreply.github.com>

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6f20a05d-6bee-4e2e-b320-12f8d9759bbc
Signed-off-by: cshung <3410332+cshung@users.noreply.github.com>
@cshung
cshung force-pushed the cshung/aslr-pie-guests branch from 09e0700 to 7f2b79e Compare July 18, 2026 18:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/enhancement For PRs adding features, improving functionality, docs, tests, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants