94646dee65
ROOT CAUSE: fexec_impl maps the 8 MB stack at STACK_TOP - STACK_SIZE = 0x7FFFFFF800000 (with STACK_TOP = 1 << 47 = 0x800000000000 on x86_64). The update_min_mmap_addr closure computes (addr + size).next_multiple_of(PAGE_SIZE) = 0x800000000000 as the new mmap_min. But 0x800000000000 is the FIRST non-canonical address on x86-64 — the canonical lower half ends at 0x7FFFFFFFFFFF. With mmap_min = 0x800000000000, every subsequent anonymous mmap fails because find_free_near can't find any hole at or above 0x800000000000. This breaks ld.so's stage2 Tcb::new(0) call (8 KB anonymous mmap), which uses .expect_notls() and panics → ud2. The init process crashes before main() with 'Invalid opcode fault' at RIP 0x21cd1. FIX: Add USER_CANONICAL_TOP_PAGE constant per architecture and cap mmap_min_addr at that value. On x86_64/aarch64 this is 0x7FFFFFFFF000 (last page in the 48-bit canonical lower half). On riscv64-sv39 this is (1 << 38) - 0x1000. On i686 this is (1 << 31) - 0x1000.
413 lines
12 KiB
Rust
413 lines
12 KiB
Rust
use core::{cell::SyncUnsafeCell, mem::offset_of, ptr::NonNull, sync::atomic::Ordering};
|
|
|
|
use syscall::*;
|
|
|
|
use crate::{
|
|
proc::{FdGuard, FdGuardUpper, ForkArgs, fork_inner},
|
|
signal::{PROC_CONTROL_STRUCT, PosixStackt, RtSigarea, SigStack, inner_fastcall},
|
|
};
|
|
use redox_protocols::protocol::{ProcCall, RtSigInfo};
|
|
|
|
use super::ForkScratchpad;
|
|
|
|
// Setup a stack starting from the very end of the address space, and then growing downwards.
|
|
pub const STACK_TOP: usize = 1 << 31;
|
|
pub const STACK_SIZE: usize = 1024 * 1024;
|
|
pub const USER_CANONICAL_TOP_PAGE: usize = (1 << 31) - 0x1000;
|
|
|
|
#[derive(Debug, Default)]
|
|
#[repr(C)]
|
|
pub struct SigArea {
|
|
pub altstack_top: usize,
|
|
pub altstack_bottom: usize,
|
|
pub tmp_eip: usize,
|
|
pub tmp_esp: usize,
|
|
pub tmp_eax: usize,
|
|
pub tmp_ebx: usize,
|
|
pub tmp_ecx: usize,
|
|
pub tmp_edx: usize,
|
|
pub tmp_edi: usize,
|
|
pub tmp_esi: usize,
|
|
pub tmp_rt_inf: RtSigInfo,
|
|
pub tmp_signo: usize,
|
|
pub tmp_id_inf: u64,
|
|
pub tmp_mm0: u64,
|
|
pub disable_signals_depth: u64,
|
|
pub last_sig_was_restart: bool,
|
|
pub last_sigstack: Option<NonNull<SigStack>>,
|
|
}
|
|
#[derive(Debug, Default)]
|
|
#[repr(C, align(16))]
|
|
pub struct ArchIntRegs {
|
|
pub fxsave: [u16; 29],
|
|
|
|
// ensure fxsave region is 16 byte aligned
|
|
pub _pad: [usize; 2], // fxsave "available" +0
|
|
|
|
pub ebp: usize, // fxsave "available" +8
|
|
pub esi: usize, // avail +12
|
|
pub edi: usize, // avail +16
|
|
pub ebx: usize, // avail +20
|
|
pub eax: usize, // avail +24
|
|
pub ecx: usize, // avail +28
|
|
pub edx: usize, // avail +32
|
|
|
|
pub eflags: usize, // avail +36
|
|
pub eip: usize, // avail +40
|
|
pub esp: usize, // avail +44
|
|
}
|
|
|
|
/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS
|
|
/// is already initialized as if it was a thread.
|
|
pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> {
|
|
let mut env = syscall::EnvRegisters::default();
|
|
|
|
let file = open_via_dup.dup(b"regs/env")?;
|
|
|
|
env.fsbase = 0;
|
|
env.gsbase = 0;
|
|
|
|
file.write(&mut env)?;
|
|
Ok(())
|
|
}
|
|
|
|
unsafe extern "fastcall" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize {
|
|
Error::mux(fork_inner(initial_rsp, args))
|
|
}
|
|
|
|
// TODO: duplicate code with x86_64
|
|
unsafe extern "cdecl" fn child_hook(scratchpad: ForkScratchpad) {
|
|
let _ = syscall::close(scratchpad.cur_filetable_fd);
|
|
unsafe {
|
|
crate::child_hook_common(crate::ChildHookCommonArgs {
|
|
new_thr_fd: FdGuard::new(scratchpad.new_thr_fd),
|
|
new_proc_fd: if scratchpad.new_proc_fd == usize::MAX {
|
|
None
|
|
} else {
|
|
Some(FdGuard::new(scratchpad.new_proc_fd))
|
|
},
|
|
})
|
|
};
|
|
}
|
|
|
|
asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: ["
|
|
mov ecx, [esp+4]
|
|
|
|
push ebp
|
|
mov ebp, esp
|
|
|
|
// Push preserved registers
|
|
push ebx
|
|
push edi
|
|
push esi
|
|
push ebp
|
|
|
|
sub esp, 32
|
|
|
|
//TODO stmxcsr [esp+16]
|
|
fnstcw [esp+24]
|
|
|
|
mov edx, esp
|
|
call {fork_impl}
|
|
|
|
jmp 2f
|
|
"] <= [fork_impl = sym fork_impl]);
|
|
|
|
asmfunction!(__relibc_internal_fork_ret: ["
|
|
// Arguments already on the stack
|
|
call {child_hook}
|
|
|
|
//TODO ldmxcsr [esp+16]
|
|
fldcw [esp+24]
|
|
|
|
xor eax, eax
|
|
|
|
.p2align 4
|
|
2:
|
|
add esp, 32
|
|
|
|
// Pop preserved registers
|
|
pop ebp
|
|
pop esi
|
|
pop edi
|
|
pop ebx
|
|
|
|
pop ebp
|
|
|
|
ret
|
|
"] <= [child_hook = sym child_hook]);
|
|
asmfunction!(__relibc_internal_sigentry: ["
|
|
// Save some registers
|
|
mov gs:[{tcb_sa_off} + {sa_tmp_esp}], esp
|
|
mov gs:[{tcb_sa_off} + {sa_tmp_eax}], eax
|
|
mov gs:[{tcb_sa_off} + {sa_tmp_edx}], edx
|
|
mov gs:[{tcb_sa_off} + {sa_tmp_ecx}], ecx
|
|
mov gs:[{tcb_sa_off} + {sa_tmp_ebx}], ebx
|
|
mov gs:[{tcb_sa_off} + {sa_tmp_edi}], edi
|
|
mov gs:[{tcb_sa_off} + {sa_tmp_esi}], esi
|
|
1:
|
|
// Read standard signal word - first for this thread
|
|
mov edx, gs:[{tcb_sc_off} + {sc_word} + 4]
|
|
mov eax, gs:[{tcb_sc_off} + {sc_word}]
|
|
and eax, edx
|
|
bsf eax, eax
|
|
jnz 9f
|
|
|
|
// Read standard signal word - for the process
|
|
lea ecx, [{pctl}]
|
|
mov eax, [ecx + {pctl_pending}]
|
|
and eax, edx
|
|
bsf eax, eax
|
|
jz 3f
|
|
|
|
// Read si_pid and si_uid, atomically.
|
|
movq gs:[{tcb_sa_off} + {sa_tmp_mm0}], mm0
|
|
movq mm0, [ecx + {pctl_sender_infos} + eax * 8]
|
|
movq gs:[{tcb_sa_off} + {sa_tmp_id_inf}], mm0
|
|
movq mm0, gs:[{tcb_sa_off} + {sa_tmp_mm0}]
|
|
|
|
// Try clearing the pending bit, otherwise retry if another thread did that first
|
|
lock btr [ecx + {pctl_pending}], eax
|
|
jnc 1b
|
|
jmp 2f
|
|
3:
|
|
// Read realtime thread and process signal word together
|
|
mov edx, [ecx + {pctl_pending} + 4]
|
|
mov eax, gs:[{tcb_sc_off} + {sc_word} + 8]
|
|
or eax, edx
|
|
and eax, gs:[{tcb_sc_off} + {sc_word} + 12]
|
|
bsf eax, eax
|
|
jz 7f // spurious signal
|
|
|
|
// If thread rather than process was specifically targeted, send the signal to it first.
|
|
bt edx, eax
|
|
jnc 8f
|
|
|
|
// SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base)
|
|
// eax ebx ecx edx esi edi
|
|
|
|
mov ebx, [{proc_fd}]
|
|
mov ecx, gs:[0]
|
|
add ecx, {tcb_sa_off} + {sa_tmp_rt_inf}
|
|
mov [ecx], eax
|
|
mov gs:[{tcb_sa_off} + {sa_tmp_signo}], eax
|
|
mov edx, {RTINF_SIZE}
|
|
mov esi, 1
|
|
lea edi, [{proc_call}]
|
|
mov eax, {SYS_CALL}
|
|
int 0x80
|
|
test eax, eax
|
|
jnz 1b
|
|
|
|
mov eax, gs:[{tcb_sa_off} + {sa_tmp_signo}]
|
|
add eax, 32
|
|
jmp 2f
|
|
8:
|
|
add eax, 32
|
|
9:
|
|
// Read si_pid and si_uid, atomically.
|
|
movq gs:[{tcb_sa_off} + {sa_tmp_mm0}], mm0
|
|
movq mm0, gs:[{tcb_sc_off} + {sc_sender_infos} + eax * 8]
|
|
movq gs:[{tcb_sa_off} + {sa_tmp_id_inf}], mm0
|
|
movq mm0, gs:[{tcb_sa_off} + {sa_tmp_mm0}]
|
|
mov edx, eax
|
|
shr edx, 5
|
|
mov ecx, eax
|
|
and ecx, 31
|
|
lock btr gs:[{tcb_sc_off} + {sc_word} + edx * 8], ecx
|
|
|
|
add eax, 64
|
|
2:
|
|
and esp, -{STACK_ALIGN}
|
|
|
|
mov edx, eax
|
|
add edx, edx
|
|
bt dword ptr [{pctl} + {pctl_actions} + edx * 8 + 4], 28
|
|
jnc 4f
|
|
|
|
mov edx, gs:[{tcb_sa_off} + {sa_altstack_top}]
|
|
cmp esp, edx
|
|
ja 3f
|
|
|
|
cmp esp, gs:[{tcb_sa_off} + {sa_altstack_bottom}]
|
|
jnbe 4f
|
|
3:
|
|
mov esp, edx
|
|
4:
|
|
// Now that we have a stack, we can finally start populating the signal stack.
|
|
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_esp}]
|
|
push dword ptr gs:[{tcb_sc_off} + {sc_saved_eip}]
|
|
push dword ptr gs:[{tcb_sc_off} + {sc_saved_eflags}]
|
|
|
|
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_edx}]
|
|
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ecx}]
|
|
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_eax}]
|
|
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ebx}]
|
|
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_edi}]
|
|
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_esi}]
|
|
push ebp
|
|
|
|
sub esp, 2 * 4 + 29 * 16
|
|
fxsave [esp]
|
|
|
|
mov [esp - 4], eax
|
|
sub esp, 48
|
|
|
|
mov ecx, esp
|
|
call {inner}
|
|
|
|
fxrstor [esp + 48]
|
|
add esp, 48 + 29 * 16 + 2 * 4
|
|
|
|
pop ebp
|
|
pop esi
|
|
pop edi
|
|
pop ebx
|
|
pop eax
|
|
pop ecx
|
|
pop edx
|
|
|
|
popfd
|
|
pop dword ptr gs:[{tcb_sa_off} + {sa_tmp_eip}]
|
|
|
|
.globl __relibc_internal_sigentry_crit_first
|
|
__relibc_internal_sigentry_crit_first:
|
|
pop esp
|
|
|
|
.globl __relibc_internal_sigentry_crit_second
|
|
__relibc_internal_sigentry_crit_second:
|
|
jmp dword ptr gs:[{tcb_sa_off} + {sa_tmp_eip}]
|
|
7:
|
|
mov eax, gs:[0]
|
|
lea esp, [eax + {tcb_sc_off} + {sc_saved_eflags}]
|
|
popfd
|
|
|
|
mov esp, gs:[{tcb_sa_off} + {sa_tmp_esp}]
|
|
|
|
mov eax, gs:[{tcb_sc_off} + {sc_saved_eip}]
|
|
mov gs:[{tcb_sa_off} + {sa_tmp_eip}], eax
|
|
|
|
mov eax, gs:[{tcb_sa_off} + {sa_tmp_eax}]
|
|
mov ebx, gs:[{tcb_sa_off} + {sa_tmp_ebx}]
|
|
mov ecx, gs:[{tcb_sa_off} + {sa_tmp_ecx}]
|
|
mov edx, gs:[{tcb_sa_off} + {sa_tmp_edx}]
|
|
mov edi, gs:[{tcb_sa_off} + {sa_tmp_edi}]
|
|
mov esi, gs:[{tcb_sa_off} + {sa_tmp_esi}]
|
|
|
|
and dword ptr gs:[{tcb_sc_off} + {sc_control}], ~1
|
|
.globl __relibc_internal_sigentry_crit_third
|
|
__relibc_internal_sigentry_crit_third:
|
|
jmp dword ptr gs:[{tcb_sa_off} + {sa_tmp_eip}]
|
|
"] <= [
|
|
inner = sym inner_fastcall,
|
|
sa_tmp_eip = const offset_of!(SigArea, tmp_eip),
|
|
sa_tmp_esp = const offset_of!(SigArea, tmp_esp),
|
|
sa_tmp_eax = const offset_of!(SigArea, tmp_eax),
|
|
sa_tmp_ebx = const offset_of!(SigArea, tmp_ebx),
|
|
sa_tmp_ecx = const offset_of!(SigArea, tmp_ecx),
|
|
sa_tmp_edx = const offset_of!(SigArea, tmp_edx),
|
|
sa_tmp_edi = const offset_of!(SigArea, tmp_edi),
|
|
sa_tmp_esi = const offset_of!(SigArea, tmp_esi),
|
|
sa_tmp_mm0 = const offset_of!(SigArea, tmp_mm0),
|
|
sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf),
|
|
sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf),
|
|
sa_tmp_signo = const offset_of!(SigArea, tmp_signo),
|
|
sa_altstack_top = const offset_of!(SigArea, altstack_top),
|
|
sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom),
|
|
sc_control = const offset_of!(Sigcontrol, control_flags),
|
|
sc_saved_eflags = const offset_of!(Sigcontrol, saved_archdep_reg),
|
|
sc_saved_eip = const offset_of!(Sigcontrol, saved_ip),
|
|
sc_word = const offset_of!(Sigcontrol, word),
|
|
sc_sender_infos = const offset_of!(Sigcontrol, sender_infos),
|
|
tcb_sa_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch),
|
|
tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control),
|
|
pctl_actions = const offset_of!(SigProcControl, actions),
|
|
pctl_sender_infos = const offset_of!(SigProcControl, sender_infos),
|
|
pctl_pending = const offset_of!(SigProcControl, pending),
|
|
pctl = sym PROC_CONTROL_STRUCT,
|
|
proc_fd = sym PROC_FD,
|
|
proc_call = sym PROC_CALL,
|
|
STACK_ALIGN = const 16,
|
|
SYS_CALL = const syscall::SYS_CALL,
|
|
RTINF_SIZE = const size_of::<RtSigInfo>(),
|
|
]);
|
|
|
|
asmfunction!(__relibc_internal_rlct_clone_ret -> usize: ["
|
|
# Load registers
|
|
pop eax
|
|
|
|
sub esp, 8
|
|
|
|
mov DWORD PTR [esp], 0x00001F80
|
|
# TODO: ldmxcsr [esp]
|
|
mov WORD PTR [esp], 0x037F
|
|
fldcw [esp]
|
|
|
|
add esp, 8
|
|
|
|
# Call entry point
|
|
call eax
|
|
|
|
ret
|
|
"] <= []);
|
|
unsafe extern "C" {
|
|
fn __relibc_internal_sigentry_crit_first();
|
|
fn __relibc_internal_sigentry_crit_second();
|
|
fn __relibc_internal_sigentry_crit_third();
|
|
}
|
|
pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt {
|
|
if stack.regs.eip == __relibc_internal_sigentry_crit_first as usize {
|
|
let stack_ptr = stack.regs.esp as *const usize;
|
|
stack.regs.esp = unsafe { stack_ptr.read() };
|
|
stack.regs.eip = unsafe { stack_ptr.sub(1).read() };
|
|
} else if stack.regs.eip == __relibc_internal_sigentry_crit_second as usize
|
|
|| stack.regs.eip == __relibc_internal_sigentry_crit_third as usize
|
|
{
|
|
stack.regs.eip = area.tmp_eip;
|
|
}
|
|
PosixStackt {
|
|
sp: stack.regs.esp as *mut (),
|
|
size: 0, // TODO
|
|
flags: 0, // TODO
|
|
}
|
|
}
|
|
pub fn arch_ret_to_sig(stack: &mut SigStack, control: &Sigcontrol) {
|
|
let orig_eip = core::mem::replace(&mut stack.regs.eip, __relibc_internal_sigentry as usize);
|
|
control.saved_ip.set(orig_eip);
|
|
control.saved_archdep_reg.set(stack.regs.eflags);
|
|
}
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe fn manually_enter_trampoline() {
|
|
let c = unsafe { &crate::Tcb::current().unwrap().os_specific.control };
|
|
c.control_flags.store(
|
|
c.control_flags.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(),
|
|
Ordering::Release,
|
|
);
|
|
c.saved_archdep_reg.set(0); // TODO: Just reset DF on x86?
|
|
|
|
unsafe {
|
|
core::arch::asm!("
|
|
call 2f
|
|
jmp 3f
|
|
2:
|
|
pop dword ptr gs:[{tcb_sc_off} + {sc_saved_eip}]
|
|
jmp __relibc_internal_sigentry
|
|
3:
|
|
",
|
|
tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control),
|
|
sc_saved_eip = const offset_of!(Sigcontrol, saved_ip),
|
|
);
|
|
}
|
|
}
|
|
/// Get current stack pointer, weak granularity guarantees.
|
|
pub fn current_sp() -> usize {
|
|
let sp: usize;
|
|
unsafe {
|
|
core::arch::asm!("mov {}, esp", out(reg) sp);
|
|
}
|
|
sp
|
|
}
|
|
|
|
pub static PROC_FD: SyncUnsafeCell<usize> = SyncUnsafeCell::new(usize::MAX);
|
|
static PROC_CALL: u64 = ProcCall::Sigdeq as u64;
|