-
Notifications
You must be signed in to change notification settings - Fork 190
Make ELF loading respect program header virtual addresses for non-PIE binaries #1530
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,7 +34,9 @@ use crate::Result; | |
| use crate::hypervisor::regs::CommonSpecialRegisters; | ||
| use crate::mem::exe::{ExeInfo, LoadInfo}; | ||
| use crate::mem::layout::SandboxMemoryLayout; | ||
| use crate::mem::memory_region::{GuestMemoryRegion, MemoryRegion, MemoryRegionFlags}; | ||
| use crate::mem::memory_region::{ | ||
| GuestMemoryRegion, MemoryRegion, MemoryRegionFlags, MemoryRegionType, | ||
| }; | ||
| use crate::mem::mgr::{GuestPageTableBuffer, SnapshotSharedMemory}; | ||
| use crate::mem::shared_mem::{ReadonlySharedMemory, SharedMemory}; | ||
| use crate::sandbox::SandboxConfiguration; | ||
|
|
@@ -322,6 +324,12 @@ impl Snapshot { | |
| let load_addr = layout.get_guest_code_address() as u64; | ||
| let base_va = exe_info.base_va(); | ||
| let entrypoint_va: u64 = exe_info.entrypoint().into(); | ||
| let loaded_size = exe_info.loaded_size() as u64; | ||
| let is_pie = exe_info.is_pie(); | ||
|
|
||
| // Determine the virtual base address for the code region and | ||
| // validate that it does not conflict with other memory regions. | ||
| let code_virt_base = layout.code_virt_base(is_pie, base_va, loaded_size)?; | ||
|
|
||
| let mut memory = vec![0; layout.get_memory_size()?]; | ||
|
|
||
|
|
@@ -355,9 +363,19 @@ impl Snapshot { | |
| executable, | ||
| }) | ||
| }; | ||
|
|
||
| // For the code region, use code_virt_base as the GVA. | ||
| // For non-PIE this is the ELF's declared base VA (non-identity mapping). | ||
| // For PIE this should equal the GPA (identity mapping). | ||
| let virt_base = if rgn.region_type == MemoryRegionType::Code { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One other place this logic (and the above non-overlapping check) could go is in |
||
| code_virt_base | ||
| } else { | ||
| rgn.guest_region.start as u64 | ||
| }; | ||
|
cshung marked this conversation as resolved.
|
||
|
|
||
| let mapping = Mapping { | ||
| phys_base: rgn.guest_region.start as u64, | ||
| virt_base: rgn.guest_region.start as u64, | ||
| virt_base, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This approach probably works for now, especially with executables where all the segments are contiguous. One other thing that we could do while we are already touching this code is switch to mapping the ELF a bit more "properly", by copying every section copied by a segment into the snapshot contiguously (similarly to how it is being done now, but without using (Unrelated, not-really-review note: the need to fix up the physical vs virtual discontinuity is continuing a trend of several things that makes me slightly question the actual utility of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed this would be the more proper approach. I'll leave it for a follow-up since it's a larger change, but it's on the radar. |
||
| len: rgn.guest_region.len() as u64, | ||
| kind, | ||
| }; | ||
|
|
@@ -375,13 +393,21 @@ impl Snapshot { | |
| - hyperlight_common::layout::SCRATCH_TOP_EXN_STACK_OFFSET | ||
| + 1; | ||
|
|
||
| let entrypoint_offset = entrypoint_va.checked_sub(base_va).ok_or_else(|| { | ||
| crate::new_error!( | ||
| "ELF entrypoint VA ({:#x}) is below base VA ({:#x})", | ||
| entrypoint_va, | ||
| base_va | ||
| ) | ||
| })?; | ||
|
|
||
| Ok(Self { | ||
| memory: ReadonlySharedMemory::from_bytes(&memory, layout.snapshot_size())?, | ||
| layout, | ||
| load_info, | ||
| stack_top_gva: exn_stack_top_gva, | ||
| sregs: None, | ||
| entrypoint: NextAction::Initialise(load_addr + entrypoint_va - base_va), | ||
| entrypoint: NextAction::Initialise(code_virt_base + entrypoint_offset), | ||
| snapshot_generation: 0, | ||
| host_functions: HostFunctionDetails { | ||
| host_functions: None, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably we should have some check that the executable mapping here does not conflict with any other mappings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call - added a conflict check that verifies the non-PIE code VA range doesn't overlap with any other memory region. This actually caught a real bug: the original --image-base=0x200000 produced a binary that extended into the PEB region. Bumped the base to 0x1000000 (16MB) to avoid this.