-
Notifications
You must be signed in to change notification settings - Fork 113
Runtime ELF patching, trampoline format changes, and rtld_audit removal #739
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: wdcui/stacked/pr1c-rewriter-interface
Are you sure you want to change the base?
Changes from all commits
ec58ba7
6818dd7
adbfd9a
0b2c432
d7ee8e8
dc399b6
0d3b29b
68c634f
231691d
7e79881
f1349cc
9d900b0
8ea3756
3d61f12
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
|
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. Now that the entire body of this has been removed, I don't think this file is needed anymore, right? |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,43 +1,6 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT license. | ||
|
|
||
| use std::path::PathBuf; | ||
|
|
||
| const RTLD_AUDIT_DIR: &str = "../litebox_rtld_audit"; | ||
|
|
||
| fn main() { | ||
| let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); | ||
| if target_arch != "x86_64" { | ||
| return; | ||
| } | ||
|
|
||
| let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); | ||
| let mut make_cmd = std::process::Command::new("make"); | ||
| make_cmd | ||
| .current_dir(RTLD_AUDIT_DIR) | ||
| .env("OUT_DIR", &out_dir) | ||
| .env("ARCH", &target_arch); | ||
| // Always build without DEBUG for the packager -- packaged binaries are | ||
| // release artifacts. | ||
| make_cmd.env_remove("DEBUG"); | ||
| // Force rebuild in case a stale artifact exists from a different config. | ||
| let _ = std::fs::remove_file(out_dir.join("litebox_rtld_audit.so")); | ||
|
|
||
| let output = make_cmd | ||
| .output() | ||
| .expect("Failed to execute make for rtld_audit"); | ||
| assert!( | ||
| output.status.success(), | ||
| "failed to build rtld_audit.so via make:\nstdout: {}\nstderr: {}", | ||
| String::from_utf8_lossy(&output.stdout), | ||
| String::from_utf8_lossy(&output.stderr), | ||
| ); | ||
| assert!( | ||
| out_dir.join("litebox_rtld_audit.so").exists(), | ||
| "Build failed to create litebox_rtld_audit.so" | ||
| ); | ||
|
|
||
| println!("cargo:rerun-if-changed={RTLD_AUDIT_DIR}/rtld_audit.c"); | ||
| println!("cargo:rerun-if-changed={RTLD_AUDIT_DIR}/Makefile"); | ||
| println!("cargo:rerun-if-changed=build.rs"); | ||
| // rtld_audit has been removed; nothing to build. | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -537,6 +537,8 @@ core::arch::global_asm!( | |
| " | ||
| .section .tbss | ||
| .align 8 | ||
| saved_restart_addr: | ||
| .quad 0 | ||
| scratch: | ||
| .quad 0 | ||
| host_sp: | ||
|
|
@@ -651,6 +653,13 @@ syscall_callback: | |
| // expectations of `interrupt_signal_handler`. | ||
| mov BYTE PTR gs:in_guest@tpoff, 0 | ||
|
|
||
| // Save guest R11 (syscall call-site restart address from the rewriter | ||
| // trampoline) to TLS before it is clobbered by the fsbase/gsbase save | ||
| // sequence below. This value is not placed in pt_regs (which holds | ||
| // RFLAGS in the r11 slot per the kernel ABI); instead it is kept in | ||
| // TLS for future SA_RESTART support. | ||
| mov gs:saved_restart_addr@tpoff, r11 | ||
|
|
||
| // Restore host fs base. | ||
| rdfsbase r11 | ||
| mov gs:guest_fsbase@tpoff, r11 | ||
|
|
@@ -660,6 +669,25 @@ syscall_callback: | |
| // Switch to the top of the guest context. | ||
| mov r11, rsp | ||
| mov rsp, fs:guest_context_top@tpoff | ||
| jmp .Lsyscall_save_regs | ||
|
|
||
| .globl syscall_callback_redzone | ||
| syscall_callback_redzone: | ||
| // Same as syscall_callback, but the trampoline has already reserved | ||
| // 128 bytes below RSP to protect the SysV red zone. | ||
| mov BYTE PTR gs:in_guest@tpoff, 0 | ||
| mov gs:saved_restart_addr@tpoff, r11 | ||
| rdfsbase r11 | ||
| mov gs:guest_fsbase@tpoff, r11 | ||
| rdgsbase r11 | ||
| wrfsbase r11 | ||
|
|
||
| // The trampoline lowered RSP by 128 bytes with LEA, so recover the | ||
| // architectural guest stack pointer before saving pt_regs. | ||
| lea r11, [rsp + 128] | ||
| mov rsp, fs:guest_context_top@tpoff | ||
|
|
||
| .Lsyscall_save_regs: | ||
|
|
||
| // TODO: save float and vector registers (xsave or fxsave) | ||
| // Save caller-saved registers | ||
|
|
@@ -678,7 +706,7 @@ syscall_callback: | |
| push r8 // pt_regs->r8 | ||
| push r9 // pt_regs->r9 | ||
| push r10 // pt_regs->r10 | ||
| push [rsp + 88] // pt_regs->r11 = rflags | ||
| push [rsp + 88] // pt_regs->r11 = rflags (matching real syscall ABI) | ||
| push rbx // pt_regs->bx | ||
| push rbp // pt_regs->bp | ||
| push r12 // pt_regs->r12 | ||
|
|
@@ -1967,6 +1995,8 @@ impl litebox::platform::StdioProvider for LinuxUserland { | |
| unsafe extern "C" { | ||
| // Defined in asm blocks above | ||
| fn syscall_callback() -> isize; | ||
| #[cfg(target_arch = "x86_64")] | ||
| fn syscall_callback_redzone() -> isize; | ||
| fn exception_callback(); | ||
| fn interrupt_callback(); | ||
| fn switch_to_guest_start(); | ||
|
|
@@ -2047,7 +2077,24 @@ impl ThreadContext<'_> { | |
|
|
||
| impl litebox::platform::SystemInfoProvider for LinuxUserland { | ||
| fn get_syscall_entry_point(&self) -> usize { | ||
| syscall_callback as *const () as usize | ||
| // When the seccomp/systrap backend is active, syscall instructions are | ||
| // trapped via SIGSYS — no binary rewriting needed. | ||
| #[cfg(feature = "systrap_backend")] | ||
| if self | ||
| .seccomp_interception_enabled | ||
| .load(std::sync::atomic::Ordering::SeqCst) | ||
| { | ||
| return 0; | ||
| } | ||
|
|
||
|
Comment on lines
+2080
to
+2089
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. I'm surprised by this conditional here. Do we need to give a 0 here? If it is not needed, it can just be kept at the non-tweaked default behavior, no? |
||
| #[cfg(target_arch = "x86_64")] | ||
| { | ||
| syscall_callback_redzone as *const () as usize | ||
| } | ||
| #[cfg(target_arch = "x86")] | ||
| { | ||
| syscall_callback as *const () as usize | ||
| } | ||
| } | ||
|
|
||
| fn get_vdso_address(&self) -> Option<usize> { | ||
|
|
@@ -2714,7 +2761,12 @@ unsafe fn interrupt_signal_handler( | |
| // FUTURE: handle trampoline code, too. This is somewhat less important | ||
| // because it's probably fine for the shim to observe a guest context that | ||
| // is inside the trampoline. | ||
| if ip == syscall_callback as *const () as usize { | ||
| #[cfg(target_arch = "x86")] | ||
| let is_at_syscall_callback = ip == syscall_callback as *const () as usize; | ||
| #[cfg(target_arch = "x86_64")] | ||
| let is_at_syscall_callback = ip == syscall_callback_redzone as *const () as usize | ||
| || ip == syscall_callback as *const () as usize; | ||
| if is_at_syscall_callback { | ||
|
Comment on lines
+2764
to
+2769
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. Seems somewhat repetitive, and also somewhat surprising. |
||
| // No need to clear `in_guest` or set interrupt; the syscall handler will | ||
| // clear `in_guest` and call into the shim. | ||
| return; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -414,6 +414,10 @@ struct TlsState { | |
| host_bp: Cell<*mut u128>, | ||
| guest_context_top: Cell<*mut litebox_common_linux::PtRegs>, | ||
| scratch: Cell<usize>, | ||
| /// Syscall call-site restart address from the rewriter trampoline, | ||
| /// saved here for future SA_RESTART support. Not stored in pt_regs | ||
| /// (which holds RFLAGS in the r11 slot per the kernel ABI). | ||
| saved_restart_addr: Cell<usize>, | ||
| is_in_guest: Cell<bool>, | ||
| interrupt: Cell<bool>, | ||
| continue_context: | ||
|
|
@@ -433,6 +437,7 @@ impl TlsState { | |
| host_bp: Cell::new(core::ptr::null_mut()), | ||
| guest_context_top: core::ptr::null_mut::<litebox_common_linux::PtRegs>().into(), | ||
| scratch: 0.into(), | ||
| saved_restart_addr: 0.into(), | ||
| is_in_guest: false.into(), | ||
| interrupt: false.into(), | ||
| continue_context: Box::default(), | ||
|
|
@@ -549,19 +554,37 @@ unsafe extern "C-unwind" fn run_thread_arch(thread_ctx: &mut ThreadContext, tls_ | |
| jmp .Ldone | ||
|
|
||
| // This entry point is called from the guest when it issues a syscall | ||
| // instruction. | ||
| // instruction. The rewriter trampoline has already: | ||
| // 1. Reserved 128 bytes below RSP to protect the SysV red zone | ||
| // 2. Loaded the call-site restart address into R11 (for SA_RESTART) | ||
| // 3. Loaded the return address into RCX | ||
| // | ||
| // At entry, the register context is the guest context with the | ||
| // return address in rcx. r11 is an available scratch register (it would | ||
| // contain rflags if the syscall instruction had actually been issued). | ||
| .globl syscall_callback | ||
| syscall_callback: | ||
| // All other registers hold guest state. | ||
| .globl syscall_callback_redzone | ||
| syscall_callback_redzone: | ||
| // Save guest R11 (restart address from rewriter trampoline) to | ||
| // TEB.ArbitraryUserPointer (gs:[0x28]) before the TLS index lookup | ||
| // clobbers R11. This slot is per-thread and the window is very | ||
| // narrow: only ~20 instructions of inline asm with no API calls, | ||
| // no Rust code, and no DLL activity, so the ntdll loader (which | ||
| // also uses this slot for debugger communication) cannot interfere. | ||
| mov gs:[0x28], r11 | ||
|
Contributor
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. Is it okay not to save/restore the original value of
Member
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. I think so. I didn't see a better way to solve this problem. |
||
| // Get the TLS state from the TLS slot and clear the in-guest flag. | ||
| mov r11d, DWORD PTR [rip + {TLS_INDEX}] | ||
| mov r11, QWORD PTR gs:[r11 * 8 + TEB_TLS_SLOTS_OFFSET] | ||
| mov BYTE PTR [r11 + {IS_IN_GUEST}], 0 | ||
| // Set rsp to the top of the guest context. | ||
| // Recover the restart address from the TEB slot and store it in TLS. | ||
| // We use SCRATCH as a temporary since all guest GPRs must be preserved | ||
| // and RSP modifications would break the stack pointer recovery below. | ||
| push QWORD PTR gs:[0x28] | ||
|
Contributor
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. stack overflow? |
||
| pop QWORD PTR [r11 + {SAVED_RESTART_ADDR}] | ||
| // Recover the architectural guest stack pointer (undo the 128-byte | ||
| // red zone reservation) and store it in SCRATCH. LEA is used instead | ||
| // of ADD to avoid clobbering RFLAGS before pushfq. | ||
| lea rsp, [rsp + 128] | ||
| mov QWORD PTR [r11 + {SCRATCH}], rsp | ||
|
|
||
| .Lsyscall_callback_common: | ||
| mov rsp, QWORD PTR [r11 + {GUEST_CONTEXT_TOP}] | ||
|
|
||
| // TODO: save float and vector registers (xsave or fxsave) | ||
|
|
@@ -581,7 +604,7 @@ syscall_callback: | |
| push r8 // pt_regs->r8 | ||
| push r9 // pt_regs->r9 | ||
| push r10 // pt_regs->r10 | ||
| push [rsp + 88] // pt_regs->r11 = rflags | ||
| push [rsp + 88] // pt_regs->r11 = rflags (matching real syscall ABI) | ||
| push rbx // pt_regs->bx | ||
| push rbp // pt_regs->bp | ||
| push r12 | ||
|
|
@@ -646,6 +669,7 @@ interrupt_callback: | |
| HOST_BP = const core::mem::offset_of!(TlsState, host_bp), | ||
| GUEST_CONTEXT_TOP = const core::mem::offset_of!(TlsState, guest_context_top), | ||
| SCRATCH = const core::mem::offset_of!(TlsState, scratch), | ||
| SAVED_RESTART_ADDR = const core::mem::offset_of!(TlsState, saved_restart_addr), | ||
| IS_IN_GUEST = const core::mem::offset_of!(TlsState, is_in_guest), | ||
| ); | ||
| } | ||
|
|
@@ -1947,7 +1971,7 @@ impl litebox::mm::allocator::MemoryProvider for WindowsUserland { | |
|
|
||
| unsafe extern "C" { | ||
| // Defined in asm blocks above | ||
| fn syscall_callback() -> isize; | ||
| fn syscall_callback_redzone() -> isize; | ||
| fn exception_callback() -> isize; | ||
| fn interrupt_callback(); | ||
| fn switch_to_guest_start(); | ||
|
|
@@ -2037,7 +2061,7 @@ impl ThreadContext<'_> { | |
|
|
||
| impl litebox::platform::SystemInfoProvider for WindowsUserland { | ||
| fn get_syscall_entry_point(&self) -> usize { | ||
| syscall_callback as *const () as usize | ||
| syscall_callback_redzone as *const () as usize | ||
| } | ||
|
|
||
| fn get_vdso_address(&self) -> Option<usize> { | ||
|
|
||
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.
Nit: reflow text