diff --git a/Cargo.lock b/Cargo.lock index e485d476b7..b24e0eda2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -227,9 +227,9 @@ version = "0.1.0" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libm" @@ -377,9 +377,9 @@ version = "0.1.2" [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -458,7 +458,7 @@ dependencies = [ [[package]] name = "redox_syscall" version = "0.5.11" -source = "git+https://gitlab.redox-os.org/redox-os/syscall.git#080f2003cd065bcc9a290230ede06da0a1ea0502" +source = "git+https://gitlab.redox-os.org/redox-os/syscall.git?branch=master#8f646a63ac9c3aa532a37ddfdc136ec19878f0d6" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index ebe3371f8e..8846727afb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,4 +81,4 @@ panic = "abort" [patch.crates-io] cc-11 = { git = "https://github.com/tea/cc-rs", branch = "riscv-abi-arch-fix", package = "cc" } -redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" } +redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git", branch = "master" } diff --git a/README.md b/README.md index 68bff0533e..27ee65dfbf 100644 --- a/README.md +++ b/README.md @@ -175,3 +175,10 @@ Before starting to contribute, read [this](CONTRIBUTING.md) document. - i686 (Intel/AMD) - x86_64 (Intel/AMD) - Aarch64 (ARM64) + +## Funding - _Unix-style Signals and Process Management_ + +This project is funded through [NGI Zero Core](https://nlnet.nl/core), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/RedoxOS-Signals). + +[NLnet foundation logo](https://nlnet.nl) +[NGI Zero Logo](https://nlnet.nl/core) diff --git a/include/bits/sys/wait.h b/include/bits/sys/wait.h index 7af6a82bbe..c76dbcab95 100644 --- a/include/bits/sys/wait.h +++ b/include/bits/sys/wait.h @@ -2,7 +2,7 @@ #define _BITS_SYS_WAIT_H #define WEXITSTATUS(s) (((s) >> 8) & 0xff) -#define WTERMSIG(s) (((s) & 0x7f) != 0) +#define WTERMSIG(s) ((s) & 0x7f) #define WSTOPSIG(s) WEXITSTATUS(s) #define WCOREDUMP(s) (((s) & 0x80) != 0) #define WIFEXITED(s) (((s) & 0x7f) == 0) diff --git a/redox-rt/Cargo.toml b/redox-rt/Cargo.toml index a69851747e..9210ba259a 100644 --- a/redox-rt/Cargo.toml +++ b/redox-rt/Cargo.toml @@ -15,3 +15,7 @@ plain = "0.2" redox_syscall = "0.5.8" generic-rt = { path = "../generic-rt" } + +[features] +proc = [] +default = ["proc"] diff --git a/redox-rt/src/arch/aarch64.rs b/redox-rt/src/arch/aarch64.rs index 0e4dbbc76a..de30ce4732 100644 --- a/redox-rt/src/arch/aarch64.rs +++ b/redox-rt/src/arch/aarch64.rs @@ -1,9 +1,10 @@ -use core::{mem::offset_of, ptr::NonNull}; +use core::{cell::SyncUnsafeCell, mem::offset_of, ptr::NonNull}; use syscall::{data::*, error::*}; use crate::{ - proc::{fork_inner, FdGuard}, + proc::{fork_inner, FdGuard, ForkArgs}, + protocol::{ProcCall, RtSigInfo}, signal::{inner_c, PosixStackt, RtSigarea, SigStack, PROC_CONTROL_STRUCT}, RtTcb, Tcb, }; @@ -20,6 +21,7 @@ pub struct SigArea { pub tmp_x1_x2: [usize; 2], pub tmp_x3_x4: [usize; 2], pub tmp_x5_x6: [usize; 2], + pub tmp_x7_x8: [usize; 2], pub tmp_sp: usize, pub onstack: u64, pub disable_signals_depth: u64, @@ -97,20 +99,24 @@ pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> { Ok(()) } -unsafe extern "C" fn fork_impl(initial_rsp: *mut usize) -> usize { - Error::mux(fork_inner(initial_rsp)) +unsafe extern "C" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize { + Error::mux(fork_inner(initial_rsp, args)) } -unsafe extern "C" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize) { +unsafe extern "C" fn child_hook(cur_filetable_fd: usize, new_proc_fd: usize, new_thr_fd: usize) { + //let _ = syscall::write(1, alloc::format!("CUR{cur_filetable_fd}PROC{new_proc_fd}THR{new_thr_fd}\n").as_bytes()); let _ = syscall::close(cur_filetable_fd); - // TODO: Currently pidfd == threadfd, but this will not be the case later. - RtTcb::current() - .thr_fd - .get() - .write(Some(FdGuard::new(new_pid_fd))); + crate::child_hook_common(crate::ChildHookCommonArgs { + new_thr_fd: FdGuard::new(new_thr_fd), + new_proc_fd: if new_proc_fd == usize::MAX { + None + } else { + Some(FdGuard::new(new_proc_fd)) + }, + }); } -asmfunction!(__relibc_internal_fork_wrapper -> usize: [" +asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: [" stp x29, x30, [sp, #-16]! stp x27, x28, [sp, #-16]! stp x25, x26, [sp, #-16]! @@ -122,7 +128,8 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: [" //TODO: store floating point regs - mov x0, sp + // x0: &ForkArgs + mov x1, sp bl {fork_impl} add sp, sp, #32 @@ -136,14 +143,14 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: [" "] <= [fork_impl = sym fork_impl]); asmfunction!(__relibc_internal_fork_ret: [" - ldp x0, x1, [sp] + ldp x0, x1, [sp], #16 + ldp x2, x3, [sp], #16 bl {child_hook} //TODO: load floating point regs mov x0, xzr - add sp, sp, #32 ldp x19, x20, [sp], #16 ldp x21, x22, [sp], #16 ldp x23, x24, [sp], #16 @@ -167,6 +174,7 @@ asmfunction!(__relibc_internal_sigentry: [" stp x1, x2, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}] stp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}] stp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}] + stp x7, x8, [x0, #{tcb_sa_off} + {sa_tmp_x7_x8}] mov x1, sp str x1, [x0, #{tcb_sa_off} + {sa_tmp_sp}] @@ -219,37 +227,47 @@ asmfunction!(__relibc_internal_sigentry: [" clrex // Load the pending set again. TODO: optimize this? + // Process pending - realtime add x1, x6, #{pctl_pending} ldaxr x2, [x1] lsr x2, x2, #32 + // Thread pending - realtime and allowset add x5, x0, #{tcb_sc_off} + {sc_word} + 8 ldar x1, [x5] - orr x2, x1, x2 - and x2, x2, x2, lsr #32 - cbz x2, 7f + orr x2, x1, x2 // combine proc and thread pending + and x2, x2, x2, lsr #32 // AND pending with allowset + cbz x2, 7f // spurious signal if realtime is clear - rbit x3, x2 - clz x3, x3 - mov x4, #31 - sub x2, x4, x3 + rbit x2, x2 + clz x2, x2 // x2 now contains sig_idx - 32 // If realtime signal was directed at thread, handle it as an idempotent signal. - lsr x3, x1, x2 - tbnz x3, #0, 5f + lsr x3, x1, x2 // x3 := x1 >> x2; x1 is thread pending + tbnz x3, #0, 5f // jump if bit is nonzero - mov x5, x0 - mov x4, x8 - mov x8, #{SYS_SIGDEQUEUE} - mov x0, x1 - add x1, x0, #{tcb_sa_off} + {sa_tmp_rt_inf} + // SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base | (flags << 8)) + // x8 x0 x1 x2 x3 x4 + + mov x5, x0 // save TCB pointer + mov x6, x2 + ldr x8, ={SYS_CALL} + adrp x0, {proc_fd} + ldr x0, [x0, #:lo12:{proc_fd}] + add x1, x5, #{tcb_sa_off} + {sa_tmp_rt_inf} + str x6, [x1] + mov x2, #{RTINF_SIZE} + adrp x4, {proc_call} + add x4, x4, :lo12:{proc_call} + mov x3, #1 svc 0 - mov x0, x5 - mov x8, x4 - cbnz x0, 1b - + mov x3, x0 + mov x0, x5 // restore TCB pointer + mov x2, x6 // restore signal number - 32 + add x1, x2, #32 // signal number + cbnz x3, 1b b 2f 5: // A realtime signal was sent to this thread, try clearing its bit. @@ -278,7 +296,7 @@ asmfunction!(__relibc_internal_sigentry: [" b 2f 3: // A standard signal was sent to this thread, try clearing its bit. - clz x1, x1 + clz w1, w1 mov x2, #31 sub x1, x2, x1 @@ -299,7 +317,7 @@ asmfunction!(__relibc_internal_sigentry: [" str x2, [x0, #{tcb_sa_off} + {sa_tmp_id_inf}] 2: ldr x3, [x0, #{tcb_sa_off} + {sa_pctl}] - add x2, x2, {pctl_actions} + add x3, x3, {pctl_actions} add x2, x3, w1, uxtb #4 // actions_base + sig_idx * sizeof Action // TODO: NOT ATOMIC (tearing allowed between regs)! ldxp x2, x3, [x2] @@ -328,8 +346,9 @@ asmfunction!(__relibc_internal_sigentry: [" stp x4, x3, [sp, #-16]! ldp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}] stp x6, x5, [sp, #-16]! - + ldp x7, x8, [x0, #{tcb_sa_off} + {sa_tmp_x7_x8}] stp x8, x7, [sp, #-16]! + stp x10, x9, [sp, #-16]! stp x12, x11, [sp, #-16]! stp x14, x13, [sp, #-16]! @@ -400,6 +419,7 @@ asmfunction!(__relibc_internal_sigentry: [" sa_tmp_x1_x2 = const offset_of!(SigArea, tmp_x1_x2), sa_tmp_x3_x4 = const offset_of!(SigArea, tmp_x3_x4), sa_tmp_x5_x6 = const offset_of!(SigArea, tmp_x5_x6), + sa_tmp_x7_x8 = const offset_of!(SigArea, tmp_x7_x8), sa_tmp_sp = const offset_of!(SigArea, tmp_sp), sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf), sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf), @@ -409,12 +429,17 @@ asmfunction!(__relibc_internal_sigentry: [" sc_sender_infos = const offset_of!(Sigcontrol, sender_infos), sc_word = const offset_of!(Sigcontrol, word), sc_flags = const offset_of!(Sigcontrol, control_flags), + proc_fd = sym PROC_FD, inner = sym inner_c, + proc_call = sym PROC_CALL, SA_ONSTACK_BIT = const 58, // (1 << 58) >> 32 = 0x0400_0000 - SYS_SIGDEQUEUE = const syscall::SYS_SIGDEQUEUE, + + SYS_CALL = const syscall::SYS_CALL, + STACK_ALIGN = const 16, REDZONE_SIZE = const 128, + RTINF_SIZE = const size_of::(), ]); asmfunction!(__relibc_internal_rlct_clone_ret: [" @@ -460,3 +485,5 @@ pub unsafe fn arch_pre(stack: &mut SigStack, os: &mut SigArea) -> PosixStackt { flags: 0, // TODO } } +pub(crate) static PROC_FD: SyncUnsafeCell = SyncUnsafeCell::new(usize::MAX); +static PROC_CALL: [usize; 1] = [ProcCall::Sigdeq as usize]; diff --git a/redox-rt/src/arch/i686.rs b/redox-rt/src/arch/i686.rs index 71319f50d5..b089a607bb 100644 --- a/redox-rt/src/arch/i686.rs +++ b/redox-rt/src/arch/i686.rs @@ -1,9 +1,10 @@ -use core::{mem::offset_of, ptr::NonNull, sync::atomic::Ordering}; +use core::{cell::SyncUnsafeCell, mem::offset_of, ptr::NonNull, sync::atomic::Ordering}; use syscall::*; use crate::{ - proc::{fork_inner, FdGuard}, + proc::{fork_inner, FdGuard, ForkArgs}, + protocol::{ProcCall, RtSigInfo}, signal::{inner_fastcall, PosixStackt, RtSigarea, SigStack, PROC_CONTROL_STRUCT}, RtTcb, }; @@ -20,12 +21,15 @@ pub struct SigArea { 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 pctl: usize, // TODO: reference pctl directly pub disable_signals_depth: u64, pub last_sig_was_restart: bool, pub last_sigstack: Option>, @@ -79,16 +83,30 @@ pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> { Ok(()) } -unsafe extern "cdecl" fn fork_impl(initial_rsp: *mut usize) -> usize { - Error::mux(fork_inner(initial_rsp)) +unsafe extern "fastcall" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize { + Error::mux(fork_inner(initial_rsp, args)) } -unsafe extern "cdecl" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize) { +// TODO: duplicate code with x86_64 +unsafe extern "cdecl" fn child_hook( + cur_filetable_fd: usize, + new_proc_fd: usize, + new_thr_fd: usize, +) { let _ = syscall::close(cur_filetable_fd); - crate::child_hook_common(FdGuard::new(new_pid_fd)); + crate::child_hook_common(crate::ChildHookCommonArgs { + new_thr_fd: FdGuard::new(new_thr_fd), + new_proc_fd: if new_proc_fd == usize::MAX { + None + } else { + Some(FdGuard::new(new_proc_fd)) + }, + }); } -asmfunction!(__relibc_internal_fork_wrapper -> usize: [" +asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: [" + mov ecx, [esp+4] + push ebp mov ebp, esp @@ -103,9 +121,9 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: [" //TODO stmxcsr [esp+16] fnstcw [esp+24] - push esp + mov edx, esp call {fork_impl} - pop esp + jmp 2f "] <= [fork_impl = sym fork_impl]); @@ -137,6 +155,9 @@ asmfunction!(__relibc_internal_sigentry: [" 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] @@ -145,9 +166,8 @@ asmfunction!(__relibc_internal_sigentry: [" bsf eax, eax jnz 9f - mov ecx, gs:[{tcb_sa_off} + {sa_pctl}] - // Read standard signal word - for the process + lea ecx, [{pctl}] mov eax, [ecx + {pctl_pending}] and eax, edx bsf eax, eax @@ -169,24 +189,31 @@ asmfunction!(__relibc_internal_sigentry: [" mov eax, gs:[{tcb_sc_off} + {sc_word} + 8] or eax, edx and eax, gs:[{tcb_sc_off} + {sc_word} + 12] - jz 7f // spurious signal bsf eax, eax + jz 7f // spurious signal - // If thread was specifically targeted, send the signal to it first. + // If thread rather than process was specifically targeted, send the signal to it first. bt edx, eax - jc 8f + jnc 8f - mov edx, ebx - lea ecx, [eax+32] - mov eax, {SYS_SIGDEQUEUE} - mov edx, gs:[0] - add edx, {tcb_sa_off} + {sa_tmp_rt_inf} + // 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 - mov ebx, edx test eax, eax jnz 1b - mov eax, ecx + mov eax, gs:[{tcb_sa_off} + {sa_tmp_signo}] + add eax, 32 jmp 2f 8: add eax, 32 @@ -228,9 +255,9 @@ asmfunction!(__relibc_internal_sigentry: [" 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 ebx - push edi - push esi + 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 @@ -274,8 +301,11 @@ __relibc_internal_sigentry_crit_second: 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 @@ -286,14 +316,17 @@ __relibc_internal_sigentry_crit_third: 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), - sa_pctl = const offset_of!(SigArea, pctl), 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), @@ -305,8 +338,11 @@ __relibc_internal_sigentry_crit_third: 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_SIGDEQUEUE = const syscall::SYS_SIGDEQUEUE, + SYS_CALL = const syscall::SYS_CALL, + RTINF_SIZE = const size_of::(), ]); asmfunction!(__relibc_internal_rlct_clone_ret -> usize: [" @@ -377,3 +413,6 @@ pub fn current_sp() -> usize { } sp } + +pub static PROC_FD: SyncUnsafeCell = SyncUnsafeCell::new(usize::MAX); +static PROC_CALL: u64 = ProcCall::Sigdeq as u64; diff --git a/redox-rt/src/arch/riscv64.rs b/redox-rt/src/arch/riscv64.rs index 3faa17118e..22b8c20008 100644 --- a/redox-rt/src/arch/riscv64.rs +++ b/redox-rt/src/arch/riscv64.rs @@ -1,5 +1,8 @@ +use core::cell::SyncUnsafeCell; + use crate::{ - proc::{fork_inner, FdGuard}, + proc::{fork_inner, FdGuard, ForkArgs}, + protocol::{ProcCall, RtSigInfo}, signal::{get_sigaltstack, inner_c, PosixStackt, RtSigarea, SigStack}, RtTcb, Tcb, }; @@ -21,6 +24,8 @@ pub struct SigArea { pub tmp_a0: u64, pub tmp_a1: u64, pub tmp_a2: u64, + pub tmp_a3: u64, + pub tmp_a4: u64, pub tmp_a7: u64, pub pctl: usize, // TODO: remove @@ -70,16 +75,23 @@ pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> { Ok(()) } -unsafe extern "C" fn fork_impl(initial_rsp: *mut usize) -> usize { - Error::mux(fork_inner(initial_rsp)) +unsafe extern "C" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize { + Error::mux(fork_inner(initial_rsp, args)) } -unsafe extern "C" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize) { +unsafe extern "C" fn child_hook(cur_filetable_fd: usize, new_proc_fd: usize, new_thr_fd: usize) { let _ = syscall::close(cur_filetable_fd); - crate::child_hook_common(FdGuard::new(new_pid_fd)); + crate::child_hook_common(crate::ChildHookCommonArgs { + new_thr_fd: FdGuard::new(new_thr_fd), + new_proc_fd: if new_proc_fd == usize::MAX { + None + } else { + Some(FdGuard::new(new_proc_fd)) + }, + }); } -asmfunction!(__relibc_internal_fork_wrapper -> usize: [" +asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: [" .attribute arch, \"rv64gc\" # rust bug 80608 addi sp, sp, -200 sd s0, 0(sp) @@ -110,7 +122,8 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: [" fsd fs11, 192(sp) addi sp, sp, -32 - mv a0, sp + // a0 is forwarded from this function + mv a1, sp jal {fork_impl} addi sp, sp, 32 @@ -148,8 +161,9 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: [" asmfunction!(__relibc_internal_fork_ret: [" .attribute arch, \"rv64gc\" # rust bug 80608 - ld a0, 0(sp) - ld a1, 8(sp) + ld a0, 0(sp) // cur_filetable_fd + ld a1, 8(sp) // new_proc_fd + ld a2, 16(sp) // new_thr_fd jal {child_hook} mv a0, x0 @@ -259,14 +273,30 @@ asmfunction!(__relibc_internal_sigentry: [" bnez t1, 10f // thread signal // otherwise, try (competitively) dequeueing realtime signal + + // SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base) + // a7 a0 a1 a2 a3 a4 + + // TODO: This SYS_CALL invocation has not yet been tested due to toolchain issues. + sd a0, ({tcb_sa_off} + {sa_tmp_a0})(t0) sd a1, ({tcb_sa_off} + {sa_tmp_a1})(t0) sd a2, ({tcb_sa_off} + {sa_tmp_a2})(t0) + sd a3, ({tcb_sa_off} + {sa_tmp_a3})(t0) + sd a4, ({tcb_sa_off} + {sa_tmp_a4})(t0) sd a7, ({tcb_sa_off} + {sa_tmp_a7})(t0) - li a0, {SYS_SIGDEQUEUE} - addi a1, t3, -32 - add a2, t0, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig + li a7, {SYS_CALL} + addi a2, t3, -32 + add a1, t0, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig + sd a2, (a1) + li a2, {RTINF_SIZE} + li a3, 1 +1337: + auipc a4, %pcrel_hi({proc_fd}) + addi a4, a4, %pcrel_lo(1337b) ecall + ld a3, ({tcb_sa_off} + {sa_tmp_a3})(t0) + ld a4, ({tcb_sa_off} + {sa_tmp_a4})(t0) bnez a0, 99b // assumes error can only be EAGAIN j 9f @@ -520,6 +550,8 @@ __relibc_internal_sigentry_crit_fifth: sa_tmp_a0 = const offset_of!(SigArea, tmp_a0), sa_tmp_a1 = const offset_of!(SigArea, tmp_a1), sa_tmp_a2 = const offset_of!(SigArea, tmp_a2), + sa_tmp_a3 = const offset_of!(SigArea, tmp_a3), + sa_tmp_a4 = const offset_of!(SigArea, tmp_a4), sa_tmp_a7 = const offset_of!(SigArea, tmp_a7), sa_tmp_ip = const offset_of!(SigArea, tmp_ip), sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf), @@ -531,7 +563,9 @@ __relibc_internal_sigentry_crit_fifth: inner = sym inner_c, pctl_off_pending = const offset_of!(SigProcControl, pending), pctl_off_sender_infos = const offset_of!(SigProcControl, sender_infos), - SYS_SIGDEQUEUE = const syscall::SYS_SIGDEQUEUE, + SYS_CALL = const syscall::SYS_CALL, + RTINF_SIZE = const size_of::(), + proc_fd = sym PROC_FD, ]); asmfunction!(__relibc_internal_rlct_clone_ret: [" @@ -620,3 +654,5 @@ pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt get_sigaltstack(area, stack.regs.int_regs[1] as usize).into() } +pub(crate) static PROC_FD: SyncUnsafeCell = SyncUnsafeCell::new(usize::MAX); +static PROC_CALL: [usize; 1] = [ProcCall::Sigdeq as usize]; diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs index cf9d69388a..3a9f1b0166 100644 --- a/redox-rt/src/arch/x86_64.rs +++ b/redox-rt/src/arch/x86_64.rs @@ -1,4 +1,5 @@ use core::{ + cell::SyncUnsafeCell, mem::offset_of, ptr::NonNull, sync::atomic::{AtomicU8, Ordering}, @@ -7,11 +8,11 @@ use core::{ use syscall::{ data::{SigProcControl, Sigcontrol}, error::*, - RtSigInfo, }; use crate::{ - proc::{fork_inner, FdGuard}, + proc::{fork_inner, FdGuard, ForkArgs}, + protocol::{ProcCall, RtSigInfo}, signal::{get_sigaltstack, inner_c, PosixStackt, RtSigarea, SigStack, PROC_CONTROL_STRUCT}, Tcb, }; @@ -29,6 +30,9 @@ pub struct SigArea { pub tmp_rdx: usize, pub tmp_rdi: usize, pub tmp_rsi: usize, + pub tmp_r8: usize, + pub tmp_r10: usize, + pub tmp_r12: usize, pub tmp_rt_inf: RtSigInfo, pub tmp_id_inf: u64, @@ -92,16 +96,27 @@ pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> { Ok(()) } -unsafe extern "sysv64" fn fork_impl(initial_rsp: *mut usize) -> usize { - Error::mux(fork_inner(initial_rsp)) +unsafe extern "sysv64" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize { + Error::mux(fork_inner(initial_rsp, args)) } -unsafe extern "sysv64" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize) { +unsafe extern "sysv64" fn child_hook( + cur_filetable_fd: usize, + new_proc_fd: usize, + new_thr_fd: usize, +) { let _ = syscall::close(cur_filetable_fd); - crate::child_hook_common(FdGuard::new(new_pid_fd)); + crate::child_hook_common(crate::ChildHookCommonArgs { + new_thr_fd: FdGuard::new(new_thr_fd), + new_proc_fd: if new_proc_fd == usize::MAX { + None + } else { + Some(FdGuard::new(new_proc_fd)) + }, + }); } -asmfunction!(__relibc_internal_fork_wrapper -> usize: [" +asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: [" push rbp mov rbp, rsp @@ -112,15 +127,16 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: [" push r14 push r15 - sub rsp, 32 + sub rsp, 48 - stmxcsr [rsp+16] - fnstcw [rsp+24] + stmxcsr [rsp+32] + fnstcw [rsp+40] - mov rdi, rsp + // rdi: &ForkArgs + mov rsi, rsp call {fork_impl} - add rsp, 80 + add rsp, 96 pop rbp ret @@ -129,14 +145,15 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: [" asmfunction!(__relibc_internal_fork_ret: [" mov rdi, [rsp] mov rsi, [rsp + 8] + mov rdx, [rsp + 16] call {child_hook} - ldmxcsr [rsp + 16] - fldcw [rsp + 24] + ldmxcsr [rsp + 32] + fldcw [rsp + 40] xor rax, rax - add rsp, 32 + add rsp, 48 pop r15 pop r14 pop r13 @@ -175,6 +192,9 @@ asmfunction!(__relibc_internal_sigentry: [" mov fs:[{tcb_sa_off} + {sa_tmp_rdx}], rdx mov fs:[{tcb_sa_off} + {sa_tmp_rdi}], rdi mov fs:[{tcb_sa_off} + {sa_tmp_rsi}], rsi + mov fs:[{tcb_sa_off} + {sa_tmp_r12}], r8 + mov fs:[{tcb_sa_off} + {sa_tmp_r12}], r10 + mov fs:[{tcb_sa_off} + {sa_tmp_r12}], r12 // First, select signal, always pick first available bit 1: @@ -212,14 +232,23 @@ asmfunction!(__relibc_internal_sigentry: [" jc 2f // if so, continue as usual // otherwise, try (competitively) dequeueing realtime signal - mov esi, eax - mov eax, {SYS_SIGDEQUEUE} - mov rdi, fs:[0] - add rdi, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig + + // SYS_CALL(fd, payload_base, payload_len, metadata_len | (flags << 8), metadata_base) + // rax rdi rsi rdx r10 r8 + + mov r12d, eax + mov rsi, fs:[0] + mov rdi, [rip+{proc_fd}] + add rsi, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig + mov rdx, {RTINF_SIZE} + mov [rsi], eax + lea r8, [rip + {proc_call_sigdeq}] + mov r10, 1 + mov eax, {SYS_CALL} syscall test eax, eax jnz 1b // assumes error can only be EAGAIN - lea eax, [esi + 32] + lea eax, [r12d + 32] jmp 9f 2: mov edx, eax @@ -272,13 +301,13 @@ asmfunction!(__relibc_internal_sigentry: [" push fs:[{tcb_sa_off} + {sa_tmp_rdx}] push rcx push fs:[{tcb_sa_off} + {sa_tmp_rax}] - push r8 + push fs:[{tcb_sa_off} + {sa_tmp_r8}] push r9 - push r10 + push fs:[{tcb_sa_off} + {sa_tmp_r10}] push r11 push rbx push rbp - push r12 + push fs:[{tcb_sa_off} + {sa_tmp_r12}] push r13 push r14 push r15 @@ -405,6 +434,9 @@ __relibc_internal_sigentry_crit_third: sa_tmp_rdx = const offset_of!(SigArea, tmp_rdx), sa_tmp_rdi = const offset_of!(SigArea, tmp_rdi), sa_tmp_rsi = const offset_of!(SigArea, tmp_rsi), + sa_tmp_r8 = const offset_of!(SigArea, tmp_r8), + sa_tmp_r10 = const offset_of!(SigArea, tmp_r10), + sa_tmp_r12 = const offset_of!(SigArea, tmp_r12), sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf), sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf), sa_altstack_top = const offset_of!(SigArea, altstack_top), @@ -424,7 +456,10 @@ __relibc_internal_sigentry_crit_third: REDZONE_SIZE = const 128, STACK_ALIGN = const 16, SA_ONSTACK_BIT = const 58, // (1 << 58) >> 32 = 0x0400_0000 - SYS_SIGDEQUEUE = const syscall::SYS_SIGDEQUEUE, + SYS_CALL = const syscall::SYS_CALL, + proc_call_sigdeq = sym PROC_CALL_SIGDEQ, + RTINF_SIZE = const size_of::(), + proc_fd = sym PROC_FD, ]); extern "C" { @@ -490,3 +525,6 @@ pub fn current_sp() -> usize { } sp } + +static PROC_CALL_SIGDEQ: u64 = ProcCall::Sigdeq as u64; +pub(crate) static PROC_FD: SyncUnsafeCell = SyncUnsafeCell::new(usize::MAX); diff --git a/redox-rt/src/lib.rs b/redox-rt/src/lib.rs index 1d0906a057..6e5beb3ef6 100644 --- a/redox-rt/src/lib.rs +++ b/redox-rt/src/lib.rs @@ -2,6 +2,7 @@ #![feature( asm_const, array_chunks, + core_intrinsics, int_roundings, let_chains, slice_ptr_get, @@ -9,18 +10,25 @@ )] #![forbid(unreachable_patterns)] -use core::cell::{SyncUnsafeCell, UnsafeCell}; +use core::{ + cell::{SyncUnsafeCell, UnsafeCell}, + mem::{size_of, MaybeUninit}, +}; use generic_rt::{ExpectTlsFree, GenericTcb}; -use syscall::{Sigcontrol, O_CLOEXEC}; +use syscall::Sigcontrol; -use self::proc::FdGuard; +use self::{ + proc::{FdGuard, STATIC_PROC_INFO}, + protocol::ProcMeta, + sync::Mutex, +}; extern crate alloc; #[macro_export] macro_rules! asmfunction( - ($name:ident $(-> $ret:ty)? : [$($asmstmt:expr),*$(,)?] <= [$($decl:ident = $(sym $symname:ident)?$(const $constval:expr)?),*$(,)?]$(,)? ) => { + ($name:ident $(($($arg:ty),*))? $(-> $ret:ty)? : [$($asmstmt:expr),*$(,)?] <= [$($decl:ident = $(sym $symname:ident)?$(const $constval:expr)?),*$(,)?]$(,)? ) => { ::core::arch::global_asm!(concat!(" .p2align 4 .section .text.", stringify!($name), ", \"ax\", @progbits @@ -32,7 +40,7 @@ macro_rules! asmfunction( "), $($decl = $(sym $symname)?$(const $constval)?),*); extern "C" { - pub fn $name() $(-> $ret)?; + pub fn $name($($(_: $arg),*)?) $(-> $ret)?; } } ); @@ -44,6 +52,7 @@ pub mod proc; #[path = "../../src/platform/auxv_defs.rs"] pub mod auxv_defs; +pub mod protocol; pub mod signal; pub mod sync; pub mod sys; @@ -60,14 +69,7 @@ impl RtTcb { unsafe { &Tcb::current().unwrap().os_specific } } pub fn thread_fd(&self) -> &FdGuard { - unsafe { - if (&*self.thr_fd.get()).is_none() { - self.thr_fd.get().write(Some(FdGuard::new( - syscall::open("/scheme/thisproc/current/open_via_dup", O_CLOEXEC).unwrap(), - ))); - } - (&*self.thr_fd.get()).as_ref().unwrap() - } + unsafe { (&*self.thr_fd.get()).as_ref().unwrap() } } } @@ -134,7 +136,8 @@ pub unsafe fn tcb_activate(_tcb: &RtTcb, tls_end: usize, tls_len: usize) { } /// Initialize redox-rt in situations where relibc is not used -pub unsafe fn initialize_freestanding() { +#[cfg(not(feature = "proc"))] +pub unsafe fn initialize_freestanding(this_thr_fd: FdGuard) -> &'static FdGuard { // TODO: This code is a hack! Integrate the ld_so TCB code into generic-rt, and then use that // (this function will need pointers to the ELF structs normally passed in auxvs), so the TCB // is initialized properly. @@ -157,8 +160,9 @@ pub unsafe fn initialize_freestanding() { page.tcb_ptr = page; page.tcb_len = syscall::PAGE_SIZE; page.tls_end = (page as *mut Tcb).cast(); + // Make sure to use ptr::write to prevent dropping the existing FdGuard - core::ptr::write(page.os_specific.thr_fd.get(), None); + page.os_specific.thr_fd.get().write(Some(this_thr_fd)); #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] unsafe { @@ -176,19 +180,124 @@ pub unsafe fn initialize_freestanding() { core::arch::asm!("mv tp, {}", in(reg) (abi_ptr + 8)); } initialize(); + + (*page.os_specific.thr_fd.get()).as_ref().unwrap() } -pub unsafe fn initialize() { - THIS_PID - .get() - .write(Some(syscall::getpid().unwrap().try_into().unwrap()).unwrap()); +pub(crate) fn read_proc_meta(proc: &FdGuard) -> syscall::Result { + let mut bytes = [0_u8; size_of::()]; + let _ = syscall::read(**proc, &mut bytes)?; + Ok(*plain::from_bytes::(&bytes).unwrap()) +} +pub unsafe fn initialize(#[cfg(feature = "proc")] proc_fd: FdGuard) { + #[cfg(feature = "proc")] + let metadata = read_proc_meta(&proc_fd).unwrap(); + + #[cfg(not(feature = "proc"))] + // Bootstrap mode, don't associate proc fds with PIDs + let metadata = ProcMeta::default(); + + #[cfg(feature = "proc")] + { + crate::arch::PROC_FD.get().write(*proc_fd); + } + + STATIC_PROC_INFO.get().write(StaticProcInfo { + pid: metadata.pid, + + #[cfg(feature = "proc")] + proc_fd: MaybeUninit::new(proc_fd), + + #[cfg(not(feature = "proc"))] + proc_fd: MaybeUninit::uninit(), + + has_proc_fd: cfg!(feature = "proc"), + }); + + #[cfg(feature = "proc")] + { + *DYNAMIC_PROC_INFO.lock() = DynamicProcInfo { + pgid: metadata.pgid, + ruid: metadata.ruid, + euid: metadata.euid, + suid: metadata.suid, + egid: metadata.egid, + rgid: metadata.rgid, + sgid: metadata.sgid, + }; + } } -static THIS_PID: SyncUnsafeCell = SyncUnsafeCell::new(0); - -unsafe fn child_hook_common(new_pid_fd: FdGuard) { - // TODO: Currently pidfd == threadfd, but this will not be the case later. - RtTcb::current().thr_fd.get().write(Some(new_pid_fd)); - THIS_PID - .get() - .write(Some(syscall::getpid().unwrap().try_into().unwrap()).unwrap()); +#[repr(C)] // TODO: is repr(C) required? +pub(crate) struct StaticProcInfo { + pid: u32, + proc_fd: MaybeUninit, + has_proc_fd: bool, +} +struct DynamicProcInfo { + pgid: u32, + euid: u32, + suid: u32, + ruid: u32, + egid: u32, + rgid: u32, + sgid: u32, +} + +static DYNAMIC_PROC_INFO: Mutex = Mutex::new(DynamicProcInfo { + pgid: u32::MAX, + ruid: u32::MAX, + euid: u32::MAX, + suid: u32::MAX, + rgid: u32::MAX, + egid: u32::MAX, + sgid: u32::MAX, +}); + +#[inline] +pub(crate) fn static_proc_info() -> &'static StaticProcInfo { + unsafe { &*STATIC_PROC_INFO.get() } +} +#[inline] +pub fn current_proc_fd() -> &'static FdGuard { + let info = static_proc_info(); + assert!(info.has_proc_fd); + unsafe { info.proc_fd.assume_init_ref() } +} + +struct ChildHookCommonArgs { + new_thr_fd: FdGuard, + new_proc_fd: Option, +} + +unsafe fn child_hook_common(args: ChildHookCommonArgs) { + // TODO: just pass PID to child rather than obtaining it via IPC? + #[cfg(feature = "proc")] + let metadata = read_proc_meta( + args.new_proc_fd + .as_ref() + .expect("must be present with proc feature"), + ) + .unwrap(); + + #[cfg(not(feature = "proc"))] + let metadata = ProcMeta::default(); + + if let Some(proc_fd) = &args.new_proc_fd { + crate::arch::PROC_FD.get().write(**proc_fd); + } + + let old_proc_fd = STATIC_PROC_INFO + .get() + .replace(StaticProcInfo { + pid: metadata.pid, + has_proc_fd: args.new_proc_fd.is_some(), + proc_fd: args + .new_proc_fd + .map_or_else(MaybeUninit::uninit, MaybeUninit::new), + }) + .proc_fd; + drop(old_proc_fd); + + let old_thr_fd = RtTcb::current().thr_fd.get().replace(Some(args.new_thr_fd)); + drop(old_thr_fd); } diff --git a/redox-rt/src/proc.rs b/redox-rt/src/proc.rs index a6a4d67e60..ba8b290b42 100644 --- a/redox-rt/src/proc.rs +++ b/redox-rt/src/proc.rs @@ -1,6 +1,17 @@ -use core::{fmt::Debug, mem::size_of}; +use core::{ + cell::SyncUnsafeCell, + fmt::Debug, + mem::{size_of, MaybeUninit}, +}; -use crate::{arch::*, auxv_defs::*}; +use crate::{ + arch::*, + auxv_defs::*, + protocol::{ProcCall, ThreadCall}, + read_proc_meta, static_proc_info, + sys::{proc_call, thread_call}, + RtTcb, StaticProcInfo, DYNAMIC_PROC_INFO, +}; use alloc::{boxed::Box, collections::BTreeMap, vec}; @@ -19,8 +30,8 @@ use goblin::elf64::{ use syscall::{ error::*, flag::{MapFlags, SEEK_SET}, - GrantDesc, GrantFlags, Map, SetSighandlerData, MAP_FIXED_NOREPLACE, MAP_SHARED, O_CLOEXEC, - PAGE_SIZE, PROT_EXEC, PROT_READ, PROT_WRITE, + CallFlags, GrantDesc, GrantFlags, Map, ProcSchemeAttrs, SetSighandlerData, MAP_FIXED_NOREPLACE, + MAP_SHARED, O_CLOEXEC, PAGE_SIZE, PROT_EXEC, PROT_READ, PROT_WRITE, }; pub enum FexecResult { @@ -30,7 +41,6 @@ pub enum FexecResult { Interp { path: Box<[u8]>, image_file: FdGuard, - open_via_dup: FdGuard, interp_override: InterpOverride, }, } @@ -53,11 +63,16 @@ pub struct ExtraInfo<'a> { pub sigprocmask: u64, /// File mode creation mask (POSIX) pub umask: u32, + /// Thread handle + pub thr_fd: usize, + /// Process handle + pub proc_fd: usize, } pub fn fexec_impl( image_file: FdGuard, - open_via_dup: FdGuard, + thread_fd: &FdGuard, + proc_fd: &FdGuard, memory_scheme_fd: &FdGuard, path: &[u8], args: A, @@ -74,15 +89,14 @@ where { // Here, we do the minimum part of loading an application, which is what the kernel used to do. // We load the executable into memory (albeit at different offsets in this executable), fix - // some misalignments, and then execute the SYS_EXEC syscall to replace the program memory - // entirely. + // some misalignments, and then switch address space. let mut header_bytes = [0_u8; size_of::
()]; pread_all(*image_file, 0, &mut header_bytes)?; let header = Header::from_bytes(&header_bytes); let grants_fd = { - let current_addrspace_fd = FdGuard::new(syscall::dup(*open_via_dup, b"addrspace")?); + let current_addrspace_fd = FdGuard::new(syscall::dup(**thread_fd, b"addrspace")?); FdGuard::new(syscall::dup(*current_addrspace_fd, b"empty")?) }; @@ -107,7 +121,7 @@ where |o| core::mem::take(&mut o.tree), ); - pread_all(*image_file as usize, header.e_phoff.into(), phs).map_err(|_| Error::new(EIO))?; + pread_all(*image_file as usize, u64::from(header.e_phoff), phs).map_err(|_| Error::new(EIO))?; for ph_idx in 0..phnum { let ph_bytes = &phs[ph_idx * phentsize..(ph_idx + 1) * phentsize]; @@ -130,12 +144,15 @@ where // PT_INTERP must come before any PT_LOAD, so we don't have to iterate twice. PT_INTERP => { let mut interp = vec![0_u8; segment.p_filesz as usize]; - pread_all(*image_file as usize, segment.p_offset.into(), &mut interp)?; + pread_all( + *image_file as usize, + u64::from(segment.p_offset), + &mut interp, + )?; return Ok(FexecResult::Interp { path: interp.into_boxed_slice(), image_file, - open_via_dup, interp_override: InterpOverride { at_entry: header.e_entry as usize, at_phnum: phnum, @@ -180,7 +197,7 @@ where }; pread_all( *image_file, - segment.p_offset.into(), + u64::from(segment.p_offset), &mut dst_memory[voff..voff + filesz], )?; } @@ -372,7 +389,12 @@ where push(AT_REDOX_INHERITED_SIGPROCMASK)?; push(extrainfo.umask as usize)?; - push(AT_REDOX_UMASK); + push(AT_REDOX_UMASK)?; + + push(extrainfo.thr_fd as usize)?; + push(AT_REDOX_THR_FD)?; + push(extrainfo.proc_fd as usize)?; + push(AT_REDOX_PROC_FD)?; push(0)?; @@ -390,7 +412,7 @@ where push(argc)?; - if let Ok(sighandler_fd) = syscall::dup(*open_via_dup, b"sighandler").map(FdGuard::new) { + if let Ok(sighandler_fd) = syscall::dup(**thread_fd, b"sighandler").map(FdGuard::new) { let _ = syscall::write( *sighandler_fd, &SetSighandlerData { @@ -400,15 +422,27 @@ where proc_control_addr: 0, }, ); + // TODO: sync with procmgr } unsafe { - deactivate_tcb(*open_via_dup)?; + deactivate_tcb(**thread_fd)?; } // TODO: Restore old name if exec failed? - if let Ok(name_fd) = syscall::dup(*open_via_dup, b"name").map(FdGuard::new) { - let _ = syscall::write(*name_fd, interp_override.as_ref().map_or(path, |o| &o.name)); + { + let mut buf = [0; 32]; + let new_name = interp_override.as_ref().map_or(path, |o| &o.name); + let len = new_name.len().min(32); + buf[..len].copy_from_slice(&new_name[..len]); + // XXX: takes &mut [] since it can mutate, but we could unsafe{}ly pass it directly + // otherwise + let _ = proc_call( + **proc_fd, + &mut buf, + CallFlags::empty(), + &[ProcCall::Rename as u64], + ); } if interp_override.is_some() { let mmap_min_fd = FdGuard::new(syscall::dup(*grants_fd, b"mmap-min-addr")?); @@ -417,7 +451,7 @@ where let _ = syscall::write(*mmap_min_fd, &usize::to_ne_bytes(aligned_last_addr)); } - let addrspace_selection_fd = FdGuard::new(syscall::dup(*open_via_dup, b"current-addrspace")?); + let addrspace_selection_fd = FdGuard::new(syscall::dup(**thread_fd, b"current-addrspace")?); let _ = syscall::write( *addrspace_selection_fd, @@ -631,32 +665,35 @@ impl Drop for MmapGuard { } } +#[repr(transparent)] pub struct FdGuard { fd: usize, - taken: bool, } impl FdGuard { - pub fn new(fd: usize) -> Self { - Self { fd, taken: false } + #[inline] + pub const fn new(fd: usize) -> Self { + Self { fd } } - pub fn take(&mut self) -> usize { - self.taken = true; - self.fd + #[inline] + pub fn take(self) -> usize { + let fd = self.fd; + core::mem::forget(self); + fd } } impl core::ops::Deref for FdGuard { type Target = usize; + #[inline] fn deref(&self) -> &Self::Target { &self.fd } } impl Drop for FdGuard { + #[inline] fn drop(&mut self) { - if !self.taken { - let _ = syscall::close(self.fd); - } + let _ = syscall::close(self.fd); } } impl Debug for FdGuard { @@ -680,9 +717,13 @@ pub fn create_set_addr_space_buf( /// Spawns a new context which will not share the same address space as the current one. File /// descriptors from other schemes are reobtained with `dup`, and grants referencing such file /// descriptors are reobtained through `fmap`. Other mappings are kept but duplicated using CoW. -pub fn fork_impl() -> Result { +pub fn fork_impl(args: &ForkArgs<'_>) -> Result { let old_mask = crate::signal::get_sigmask()?; - let pid = unsafe { Error::demux(__relibc_internal_fork_wrapper())? }; + let pid = unsafe { + Error::demux(__relibc_internal_fork_wrapper( + args as *const ForkArgs as usize, + ))? + }; if pid == 0 { crate::signal::set_sigmask(Some(old_mask), None)?; @@ -690,36 +731,52 @@ pub fn fork_impl() -> Result { Ok(pid) } -pub fn fork_inner(initial_rsp: *mut usize) -> Result { - let (cur_filetable_fd, new_pid_fd, new_pid); +pub enum ForkArgs<'a> { + Init { + this_thr_fd: &'a FdGuard, + auth: &'a FdGuard, + }, + Managed, +} + +pub fn fork_inner(initial_rsp: *mut usize, args: &ForkArgs) -> Result { + let (cur_filetable_fd, new_proc_fd, new_thr_fd, new_pid); { - let cur_pid_fd = FdGuard::new(syscall::open( - "/scheme/thisproc/current/open_via_dup", - O_CLOEXEC, - )?); - (new_pid_fd, new_pid) = new_child_process()?; - - copy_str(*cur_pid_fd, *new_pid_fd, "name")?; + let cur_thr_fd = match args { + ForkArgs::Init { this_thr_fd, .. } => this_thr_fd, + ForkArgs::Managed => RtTcb::current().thread_fd(), + }; + let NewChildProc { + proc_fd, + thr_fd, + pid, + } = new_child_process(args)?; + new_proc_fd = proc_fd; + new_thr_fd = thr_fd; + new_pid = pid; // Copy existing files into new file table, but do not reuse the same file table (i.e. new // parent FDs will not show up for the child). { - cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?); + cur_filetable_fd = FdGuard::new(syscall::dup(**cur_thr_fd, b"filetable")?); // This must be done before the address space is copied. unsafe { + let proc_fd = new_proc_fd.as_ref().map_or(usize::MAX, |p| **p); + //let _ = syscall::write(1, alloc::format!("FDTBL{}PROC{}THR{}\n", *cur_filetable_fd, proc_fd, *new_thr_fd).as_bytes()); initial_rsp.write(*cur_filetable_fd); - initial_rsp.add(1).write(*new_pid_fd); + initial_rsp.add(1).write(proc_fd); + initial_rsp.add(2).write(*new_thr_fd); } } // CoW-duplicate address space. { let new_addr_space_sel_fd = - FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?); + FdGuard::new(syscall::dup(*new_thr_fd, b"current-addrspace")?); - let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?); + let cur_addr_space_fd = FdGuard::new(syscall::dup(**cur_thr_fd, b"addrspace")?); let new_addr_space_fd = FdGuard::new(syscall::dup(*cur_addr_space_fd, b"exclusive")?); let mut grant_desc_buf = [GrantDesc::default(); 16]; @@ -795,14 +852,28 @@ pub fn fork_inner(initial_rsp: *mut usize) -> Result { // Do this after the address space is cloned, since the kernel will get a shared // reference to the TCB and whatever pages stores the signal proc control struct. { - let new_sighandler_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sighandler")?); + let new_sighandler_fd = FdGuard::new(syscall::dup(*new_thr_fd, b"sighandler")?); let _ = syscall::write( *new_sighandler_fd, &crate::signal::current_setsighandler_struct(), )?; } + if let Some(ref proc_fd) = new_proc_fd { + proc_call( + **proc_fd, + &mut [], + CallFlags::empty(), + &[ProcCall::SyncSigPctl as u64], + )?; + thread_call( + *new_thr_fd, + &mut [], + CallFlags::empty(), + &[ThreadCall::SyncSigTctl as u64], + )?; + } } - copy_env_regs(*cur_pid_fd, *new_pid_fd)?; + copy_env_regs(**cur_thr_fd, *new_thr_fd)?; } // Copy the file table. We do this last to ensure that all previously used file descriptors are // closed. The only exception -- the filetable selection fd and the current filetable fd -- @@ -811,59 +882,115 @@ pub fn fork_inner(initial_rsp: *mut usize) -> Result { // TODO: Use file descriptor forwarding or something similar to avoid copying the file // table in the kernel. let new_filetable_fd = FdGuard::new(syscall::dup(*cur_filetable_fd, b"copy")?); - let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?); + let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_thr_fd, b"current-filetable")?); let _ = syscall::write( *new_filetable_sel_fd, &usize::to_ne_bytes(*new_filetable_fd), )?; } - let start_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"start")?); + let start_fd = FdGuard::new(syscall::dup(*new_thr_fd, b"start")?); let _ = syscall::write(*start_fd, &[0])?; Ok(new_pid) } -pub fn new_child_process() -> Result<(FdGuard, usize)> { - // Create a new context (fields such as uid/gid will be inherited from the current context). - let fd = FdGuard::new(syscall::open( - "/scheme/thisproc/new/open_via_dup", - O_CLOEXEC, - )?); +struct NewChildProc { + proc_fd: Option, - // Extract pid. - let mut buffer = [0_u8; 64]; - let len = syscall::fpath(*fd, &mut buffer)?; - let buffer = buffer.get(..len).ok_or(Error::new(ENAMETOOLONG))?; - - let colon_idx = buffer - .iter() - .position(|c| *c == b':') - .ok_or(Error::new(EINVAL))?; - let slash_idx = buffer - .iter() - .skip(colon_idx) - .position(|c| *c == b'/') - .ok_or(Error::new(EINVAL))? - + colon_idx; - let pid_bytes = buffer - .get(colon_idx + 1..slash_idx) - .ok_or(Error::new(EINVAL))?; - let pid_str = core::str::from_utf8(pid_bytes).map_err(|_| Error::new(EINVAL))?; - let pid = pid_str.parse::().map_err(|_| Error::new(EINVAL))?; - - Ok((fd, pid)) + thr_fd: FdGuard, + pid: usize, } -pub fn copy_str(cur_pid_fd: usize, new_pid_fd: usize, key: &str) -> Result<()> { - let cur_name_fd = FdGuard::new(syscall::dup(cur_pid_fd, key.as_bytes())?); - let new_name_fd = FdGuard::new(syscall::dup(new_pid_fd, key.as_bytes())?); +pub fn new_child_process(args: &ForkArgs<'_>) -> Result { + match *args { + ForkArgs::Managed => { + let proc_info = crate::static_proc_info(); + assert!( + proc_info.has_proc_fd, + "cannot use ForkArgs::Managed without an existing proc info" + ); + let this_proc_fd = unsafe { proc_info.proc_fd.assume_init_ref() }; + let child_proc_fd = FdGuard::new(syscall::dup(**this_proc_fd, b"fork")?); + let only_thread_fd = FdGuard::new(syscall::dup(*child_proc_fd, b"thread-0")?); + let meta = read_proc_meta(&child_proc_fd)?; + Ok(NewChildProc { + proc_fd: Some(child_proc_fd), + thr_fd: only_thread_fd, + pid: meta.pid as usize, + }) + } + #[cfg(feature = "proc")] + ForkArgs::Init { .. } => unreachable!(), - // TODO: Max path size? - let mut buf = [0_u8; 256]; - let len = syscall::read(*cur_name_fd, &mut buf)?; - let buf = buf.get(..len).ok_or(Error::new(ENAMETOOLONG))?; - - syscall::write(*new_name_fd, &buf)?; - - Ok(()) + #[cfg(not(feature = "proc"))] + ForkArgs::Init { this_thr_fd, auth } => { + let thr_fd = FdGuard::new(syscall::dup(**auth, b"new-context")?); + let buf = ProcSchemeAttrs { + pid: 0, + euid: 0, + egid: 0, + ens: 1, + debug_name: { + let mut buf = [0; 32]; + let src = b"[init]"; + buf[..src.len()].copy_from_slice(src); + buf + }, + }; + let attr_fd = FdGuard::new(syscall::dup( + *thr_fd, + alloc::format!("auth-{}-attrs", **auth).as_bytes(), + )?); + let _ = syscall::write(*attr_fd, &buf)?; + Ok(NewChildProc { + thr_fd, + pid: 1, // dummy fd to distinguish child from parent + proc_fd: None, + }) + } + } } + +pub unsafe fn make_init() -> [&'static FdGuard; 2] { + let proc_fd = FdGuard::new( + syscall::open("/scheme/proc/init", syscall::O_CLOEXEC).expect("failed to create init"), + ); + syscall::sendfd( + *proc_fd, + syscall::dup(**RtTcb::current().thread_fd(), &[]).unwrap(), + 0, + 0, + ) + .expect("failed to assign current thread to init process"); + + let managed_thr_fd = FdGuard::new( + syscall::dup(*proc_fd, b"thread-0").expect("failed to get managed thread for init"), + ); + + let managed_thr_fd = (*RtTcb::current().thr_fd.get()).insert(managed_thr_fd); + + STATIC_PROC_INFO.get().write(crate::StaticProcInfo { + pid: 1, + proc_fd: MaybeUninit::new(proc_fd), + has_proc_fd: true, + }); + *DYNAMIC_PROC_INFO.lock() = crate::DynamicProcInfo { + pgid: 1, + ruid: 0, + euid: 0, + suid: 0, + rgid: 0, + egid: 0, + sgid: 0, + }; + [ + (*STATIC_PROC_INFO.get()).proc_fd.assume_init_ref(), + managed_thr_fd, + ] +} +pub(crate) static STATIC_PROC_INFO: SyncUnsafeCell = + SyncUnsafeCell::new(StaticProcInfo { + pid: 0, + proc_fd: MaybeUninit::zeroed(), + has_proc_fd: false, + }); diff --git a/redox-rt/src/protocol.rs b/redox-rt/src/protocol.rs new file mode 100644 index 0000000000..75fe33f8ac --- /dev/null +++ b/redox-rt/src/protocol.rs @@ -0,0 +1,196 @@ +use bitflags::bitflags; + +#[derive(Clone, Copy, Debug, Default)] +#[repr(C)] +pub struct ProcMeta { + pub pid: u32, + pub pgid: u32, + pub ppid: u32, + pub ruid: u32, + pub euid: u32, + pub suid: u32, + pub rgid: u32, + pub egid: u32, + pub sgid: u32, + pub ens: u32, + pub rns: u32, +} +unsafe impl plain::Plain for ProcMeta {} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(usize)] +pub enum ProcCall { + Waitpid = 0, + Setrens = 1, + Exit = 2, + Waitpgid = 3, + SetResugid = 4, + Setpgid = 5, + Getsid = 6, + Setsid = 7, + Kill = 8, + Sigq = 9, + + // TODO: replace with sendfd equivalent syscall for sending memory + SyncSigPctl = 10, + Sigdeq = 11, + Getppid = 12, + Rename = 13, +} +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(usize)] +pub enum ThreadCall { + // TODO: replace with sendfd equivalent syscall for sending memory, or force userspace to + // obtain its TCB memory from this server + SyncSigTctl = 0, + SignalThread = 1, +} + +impl ProcCall { + pub fn try_from_raw(raw: usize) -> Option { + Some(match raw { + 0 => Self::Waitpid, + 1 => Self::Setrens, + 2 => Self::Exit, + 3 => Self::Waitpgid, + 4 => Self::SetResugid, + 5 => Self::Setpgid, + 6 => Self::Getsid, + 7 => Self::Setsid, + 8 => Self::Kill, + 9 => Self::Sigq, + 10 => Self::SyncSigPctl, + 11 => Self::Sigdeq, + 12 => Self::Getppid, + 13 => Self::Rename, + _ => return None, + }) + } +} +impl ThreadCall { + pub fn try_from_raw(raw: usize) -> Option { + Some(match raw { + 0 => Self::SyncSigTctl, + 1 => Self::SignalThread, + _ => return None, + }) + } +} + +bitflags! { + #[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] + pub struct WaitFlags: usize { + const WNOHANG = 0x01; + const WUNTRACED = 0x02; + const WCONTINUED = 0x08; + } +} +/// True if status indicates the child is stopped. +pub fn wifstopped(status: usize) -> bool { + (status & 0xff) == 0x7f +} + +/// If wifstopped(status), the signal that stopped the child. +pub fn wstopsig(status: usize) -> usize { + (status >> 8) & 0xff +} + +/// True if status indicates the child continued after a stop. +pub fn wifcontinued(status: usize) -> bool { + status == 0xffff +} + +/// True if STATUS indicates termination by a signal. +pub fn wifsignaled(status: usize) -> bool { + ((status & 0x7f) + 1) as i8 >= 2 +} + +/// If wifsignaled(status), the terminating signal. +pub fn wtermsig(status: usize) -> usize { + status & 0x7f +} + +/// True if status indicates normal termination. +pub fn wifexited(status: usize) -> bool { + wtermsig(status) == 0 +} + +/// If wifexited(status), the exit status. +pub fn wexitstatus(status: usize) -> usize { + (status >> 8) & 0xff +} + +/// True if status indicates a core dump was created. +pub fn wcoredump(status: usize) -> bool { + (status & 0x80) != 0 +} +#[derive(Clone, Copy, Debug)] +pub enum ProcKillTarget { + ThisGroup, + SingleProc(usize), + ProcGroup(usize), + All, +} +impl ProcKillTarget { + pub fn raw(self) -> usize { + match self { + Self::ThisGroup => 0, + Self::SingleProc(p) => p, + Self::ProcGroup(g) => usize::wrapping_neg(g), + Self::All => usize::wrapping_neg(1), + } + } + pub fn from_raw(raw: usize) -> Self { + let raw = raw as isize; + if raw == 0 { + Self::ThisGroup + } else if raw == -1 { + Self::All + } else if raw < 0 { + Self::ProcGroup(raw as usize) + } else { + Self::SingleProc(raw as usize) + } + } +} +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct RtSigInfo { + pub arg: usize, + pub code: i32, + pub uid: u32, + pub pid: u32, // TODO: usize? +} +unsafe impl plain::Plain for RtSigInfo {} + +pub const SIGHUP: usize = 1; +pub const SIGINT: usize = 2; +pub const SIGQUIT: usize = 3; +pub const SIGILL: usize = 4; +pub const SIGTRAP: usize = 5; +pub const SIGABRT: usize = 6; +pub const SIGBUS: usize = 7; +pub const SIGFPE: usize = 8; +pub const SIGKILL: usize = 9; +pub const SIGUSR1: usize = 10; +pub const SIGSEGV: usize = 11; +pub const SIGUSR2: usize = 12; +pub const SIGPIPE: usize = 13; +pub const SIGALRM: usize = 14; +pub const SIGTERM: usize = 15; +pub const SIGSTKFLT: usize = 16; +pub const SIGCHLD: usize = syscall::SIGCHLD; +pub const SIGCONT: usize = 18; +pub const SIGSTOP: usize = 19; +pub const SIGTSTP: usize = syscall::SIGTSTP; +pub const SIGTTIN: usize = syscall::SIGTTIN; +pub const SIGTTOU: usize = syscall::SIGTTOU; +pub const SIGURG: usize = 23; +pub const SIGXCPU: usize = 24; +pub const SIGXFSZ: usize = 25; +pub const SIGVTALRM: usize = 26; +pub const SIGPROF: usize = 27; +pub const SIGWINCH: usize = 28; +pub const SIGIO: usize = 29; +pub const SIGPWR: usize = 30; +pub const SIGSYS: usize = 31; diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index f303383957..5810398232 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -1,12 +1,23 @@ -use core::{ffi::c_int, mem::MaybeUninit, ptr::NonNull, sync::atomic::Ordering}; +use core::{ffi::c_int, ptr::NonNull, sync::atomic::Ordering}; use syscall::{ - data::AtomicU64, Error, RawAction, Result, RtSigInfo, SenderInfo, SetSighandlerData, + data::AtomicU64, CallFlags, Error, RawAction, Result, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, SigcontrolFlags, TimeSpec, EAGAIN, EINTR, EINVAL, ENOMEM, EPERM, - SIGCHLD, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGWINCH, }; -use crate::{arch::*, proc::FdGuard, sync::Mutex, RtTcb, Tcb}; +use crate::{ + arch::*, + current_proc_fd, + proc::FdGuard, + protocol::{ + ProcCall, RtSigInfo, ThreadCall, SIGCHLD, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, + SIGURG, SIGWINCH, + }, + static_proc_info, + sync::Mutex, + sys::{proc_call, this_thread_call}, + RtTcb, Tcb, +}; #[cfg(target_arch = "x86_64")] static CPUID_EAX1_ECX: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0); @@ -149,11 +160,18 @@ unsafe fn inner(stack: &mut SigStack) { let handler = match sigaction.kind { SigactionKind::Ignore => { - panic!("ctl {:x?} signal {}", os.control, stack.sig_num) + panic!("ctl {:#x?} signal {}", os.control, stack.sig_num) } SigactionKind::Default => { - syscall::exit(stack.sig_num as usize); - unreachable!(); + let sig = (stack.sig_num & 0x3f) as u8; + + let _ = proc_call( + **current_proc_fd(), + &mut [], + CallFlags::empty(), + &[ProcCall::Exit as u64, u64::from(sig) << 8], + ); + core::intrinsics::abort() } SigactionKind::Handled { handler } => handler, }; @@ -270,7 +288,10 @@ fn get_allowset_raw(words: &[AtomicU64; 2]) -> u64 { (words[0].load(Ordering::Relaxed) >> 32) | ((words[1].load(Ordering::Relaxed) >> 32) << 32) } /// Sets mask from old to new, returning what was pending at the time. -fn set_allowset_raw(words: &[AtomicU64; 2], old: u64, new: u64) -> u64 { +fn set_allowset_raw(words: &[AtomicU64; 2], old: u64, new_raw: u64) -> u64 { + // TODO: should these bits always be set, or never be set? + let new = new_raw | ALLOWSET_ALWAYS; + // This assumes *only this thread* can change the allowset. If this rule is broken, the use of // fetch_add will corrupt the words entirely. fetch_add is very efficient on x86, being // generated as LOCK XADD which is the fastest RMW instruction AFAIK. @@ -285,6 +306,7 @@ fn set_allowset_raw(words: &[AtomicU64; 2], old: u64, new: u64) -> u64 { prev_w0 | (prev_w1 << 32) } +const ALLOWSET_ALWAYS: u64 = sig_bit(SIGSTOP as u32) | sig_bit(SIGKILL as u32); fn modify_sigmask(old: Option<&mut u64>, op: Option u64>) -> Result<()> { let _guard = tmp_disable_signals(); let ctl = current_sigctl(); @@ -375,9 +397,29 @@ fn convert_old(action: &RawAction) -> Sigaction { } pub fn sigaction(signal: u8, new: Option<&Sigaction>, old: Option<&mut Sigaction>) -> Result<()> { - if matches!(usize::from(signal), 0 | 32 | SIGKILL | SIGSTOP | 65..) { + // TODO: Now that the goal of keeping logic out of the IPC backend, no longer holds when + // procmgr has taken over signal handling from the kernel, it would probably make sense to make + // parts of this function an IPC call, for synchronization purposes. Apart from SA_RESETHAND + // logic which may need to be fast, regular sigaction is typically in the 'configuration' + // category, allowed to be slower. + + if matches!(usize::from(signal), 0 | 32 | 65..) { return Err(Error::new(EINVAL)); } + if matches!(usize::from(signal), SIGKILL | SIGSTOP) { + if new.is_some() { + return Err(Error::new(EINVAL)); + } + if let Some(old) = old { + // TODO: Is this the correct value to set it to? + *old = Sigaction { + kind: SigactionKind::Default, + mask: 0, + flags: SigactionFlags::empty(), + }; + } + return Ok(()); + } let _sigguard = tmp_disable_signals(); let ctl = current_sigctl(); @@ -398,8 +440,16 @@ pub fn sigaction(signal: u8, new: Option<&Sigaction>, old: Option<&mut Sigaction let (mask, flags, handler) = match (usize::from(signal), new.kind) { (_, SigactionKind::Ignore) | (SIGURG | SIGWINCH, SigactionKind::Default) => { - // TODO: POSIX specifies that pending signals shall be discarded if set to SIG_IGN by - // sigaction. + let sig_group = (signal - 1) / 32; + let sig_idx = (signal - 1) % 32; + + // TODO: relibc and the procmgr has access to all threads, redox_rt doesn't currently. + // Do this for all threads! + ctl.word[usize::from(sig_group)].fetch_and(!(1 << sig_idx), Ordering::Relaxed); + PROC_CONTROL_STRUCT + .pending + .fetch_and(!sig_bit(signal.into()), Ordering::Relaxed); + // TODO: handle tmp_disable_signals ( MASK_DONTCARE, @@ -497,7 +547,7 @@ bitflags::bitflags! { const STORED_FLAGS: u32 = 0xfe00_0000; fn default_handler(sig: c_int) { - syscall::exit(sig as usize); + unreachable!(); } #[derive(Clone, Copy)] @@ -529,8 +579,8 @@ const fn sig_bit(sig: u32) -> u64 { 1 << (sig - 1) } -pub fn setup_sighandler(tcb: &RtTcb) { - { +pub fn setup_sighandler(tcb: &RtTcb, first_thread: bool) { + if first_thread { let _guard = SIGACTIONS_LOCK.lock(); for (sig_idx, action) in PROC_CONTROL_STRUCT.actions.iter().enumerate() { let sig = sig_idx + 1; @@ -554,8 +604,7 @@ pub fn setup_sighandler(tcb: &RtTcb) { // equivalent to not using any altstack at all (the default). arch.altstack_top = usize::MAX; arch.altstack_bottom = 0; - // TODO - #[cfg(any(target_arch = "x86", target_arch = "aarch64", target_arch = "riscv64"))] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] { arch.pctl = core::ptr::addr_of!(PROC_CONTROL_STRUCT) as usize; } @@ -574,6 +623,12 @@ pub fn setup_sighandler(tcb: &RtTcb) { syscall::dup(**tcb.thread_fd(), b"sighandler").expect("failed to open sighandler fd"), ); let _ = syscall::write(*fd, &data).expect("failed to write to sighandler fd"); + this_thread_call( + &mut [], + CallFlags::empty(), + &[ThreadCall::SyncSigTctl as u64], + ) + .expect("failed to sync signal tctl"); // TODO: Inherited set of ignored signals set_sigmask(Some(0), None); @@ -684,17 +739,25 @@ pub fn await_signal_async(inner_allowset: u64) -> Result { }, &mut TimeSpec::default(), ); - set_allowset_raw(&control.word, inner_allowset, old_allowset); if res == Err(Error::new(EINTR)) { unsafe { manually_enter_trampoline(); } } + // POSIX says it shall restore the mask to what it was prior to the call, which is interpreted + // as allowing any changes to sigprocmask inside the signal handler, to be discarded. + set_allowset_raw(&control.word, inner_allowset, old_allowset); res?; unreachable!() } +/*#[no_mangle] +pub extern "C" fn __redox_rt_debug_sigctl() { + let tcb = &RtTcb::current().control; + let _ = syscall::write(1, alloc::format!("SIGCTL: {tcb:#x?}\n").as_bytes()); +}*/ + // TODO: deadline-based API pub fn await_signal_sync(inner_allowset: u64, timeout: Option<&TimeSpec>) -> Result { let _guard = tmp_disable_signals(); @@ -764,15 +827,17 @@ fn try_claim_single(sig_idx: u32, thread_control: Option<&Sigcontrol>) -> Option if sig_group == 1 && thread_control.is_none() { // Queued (realtime) signal - let mut ret = MaybeUninit::::uninit(); - let rt_inf = unsafe { - syscall::syscall2( - syscall::SYS_SIGDEQUEUE, - ret.as_mut_ptr() as usize, - sig_idx as usize - 32, + let rt_inf: RtSigInfo = unsafe { + let mut buf = [0_u8; size_of::()]; + buf[..4].copy_from_slice(&(sig_idx - 32).to_ne_bytes()); + proc_call( + **static_proc_info().proc_fd.assume_init_ref(), + &mut buf, + CallFlags::empty(), + &[ProcCall::Sigdeq as u64], ) .ok()?; - ret.assume_init() + core::mem::transmute(buf) }; Some(SiginfoAbi { si_signo: sig_idx as i32 + 1, diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs index 2f9bd82c30..82ece48fdc 100644 --- a/redox-rt/src/sys.rs +++ b/redox-rt/src/sys.rs @@ -1,24 +1,31 @@ use core::{ + mem::size_of, ptr::addr_of, sync::atomic::{AtomicU32, Ordering}, }; use syscall::{ - error::{Error, Result, EINTR}, - RtSigInfo, TimeSpec, + error::{self, Error, Result, EINTR}, + CallFlags, TimeSpec, EINVAL, ERESTART, }; -use crate::{arch::manually_enter_trampoline, proc::FdGuard, signal::tmp_disable_signals, Tcb}; +use crate::{ + arch::manually_enter_trampoline, + protocol::{ProcCall, ProcKillTarget, RtSigInfo, ThreadCall, WaitFlags}, + signal::tmp_disable_signals, + DynamicProcInfo, RtTcb, Tcb, DYNAMIC_PROC_INFO, +}; #[inline] -fn wrapper(restart: bool, mut f: impl FnMut() -> Result) -> Result { +fn wrapper(restart: bool, erestart: bool, mut f: impl FnMut() -> Result) -> Result { loop { let _guard = tmp_disable_signals(); let rt_sigarea = unsafe { &Tcb::current().unwrap().os_specific }; let res = f(); + let code = if erestart { ERESTART } else { EINTR }; if let Err(err) = res - && err == Error::new(EINTR) + && err == Error::new(code) { unsafe { manually_enter_trampoline(); @@ -35,49 +42,60 @@ fn wrapper(restart: bool, mut f: impl FnMut() -> Result) -> Result { // TODO: uninitialized memory? #[inline] pub fn posix_read(fd: usize, buf: &mut [u8]) -> Result { - wrapper(true, || syscall::read(fd, buf)) + wrapper(true, false, || syscall::read(fd, buf)) } #[inline] pub fn posix_write(fd: usize, buf: &[u8]) -> Result { - wrapper(true, || syscall::write(fd, buf)) + wrapper(true, false, || syscall::write(fd, buf)) } #[inline] -pub fn posix_kill(pid: usize, sig: usize) -> Result<()> { - match wrapper(false, || syscall::kill(pid, sig)) { - Ok(_) | Err(Error { errno: EINTR }) => Ok(()), +pub fn posix_kill(target: ProcKillTarget, sig: usize) -> Result<()> { + match wrapper(false, true, || { + this_proc_call( + &mut [], + CallFlags::empty(), + &[ProcCall::Kill as u64, target.raw() as u64, sig as u64], + ) + }) { + Ok(_) | Err(Error { errno: ERESTART }) => Ok(()), Err(error) => Err(error), } } #[inline] pub fn posix_sigqueue(pid: usize, sig: usize, arg: usize) -> Result<()> { - let siginf = RtSigInfo { + let mut siginf = RtSigInfo { arg, code: -1, // TODO: SI_QUEUE constant uid: 0, // TODO pid: posix_getpid(), }; - match wrapper(false, || unsafe { - syscall::syscall3(syscall::SYS_SIGENQUEUE, pid, sig, addr_of!(siginf) as usize) + match wrapper(false, true, || { + this_proc_call( + unsafe { plain::as_mut_bytes(&mut siginf) }, + CallFlags::empty(), + &[ProcCall::Sigq as u64, pid as u64, sig as u64], + ) }) { - Ok(_) | Err(Error { errno: EINTR }) => Ok(()), + Ok(_) + | Err(Error { + errno: error::EINTR, + }) => Ok(()), Err(error) => Err(error), } } #[inline] pub fn posix_getpid() -> u32 { // SAFETY: read-only except during program/fork child initialization - unsafe { crate::THIS_PID.get().read() } + unsafe { addr_of!((*crate::STATIC_PROC_INFO.get()).pid).read() } } #[inline] -pub fn posix_killpg(pgrp: usize, sig: usize) -> Result<()> { - match wrapper(false, || syscall::kill(usize::wrapping_neg(pgrp), sig)) { - Ok(_) | Err(Error { errno: EINTR }) => Ok(()), - Err(error) => Err(error), - } +pub fn posix_getppid() -> u32 { + this_proc_call(&mut [], CallFlags::empty(), &[ProcCall::Getppid as u64]).expect("cannot fail") + as u32 } #[inline] pub unsafe fn sys_futex_wait(addr: *mut u32, val: u32, deadline: Option<&TimeSpec>) -> Result<()> { - wrapper(true, || { + wrapper(true, false, || { syscall::syscall5( syscall::SYS_FUTEX, addr as usize, @@ -101,19 +119,91 @@ pub unsafe fn sys_futex_wake(addr: *mut u32, num: u32) -> Result { ) .map(|awoken| awoken as u32) } -pub fn sys_waitpid(pid: usize, status: &mut usize, flags: usize) -> Result { - wrapper(true, || { - syscall::waitpid( - pid, - status, - syscall::WaitFlags::from_bits(flags).expect("waitpid: invalid bit pattern"), +pub fn sys_call( + fd: usize, + payload: &mut [u8], + flags: CallFlags, + metadata: &[u64], +) -> Result { + unsafe { + syscall::syscall5( + syscall::SYS_CALL, + fd, + payload.as_mut_ptr() as usize, + payload.len(), + metadata.len() | flags.bits(), + metadata.as_ptr() as usize, + ) + } +} +pub fn this_proc_call(payload: &mut [u8], flags: CallFlags, metadata: &[u64]) -> Result { + proc_call(**crate::current_proc_fd(), payload, flags, metadata) +} +pub fn proc_call( + proc_fd: usize, + payload: &mut [u8], + flags: CallFlags, + metadata: &[u64], +) -> Result { + sys_call(proc_fd, payload, flags, metadata) +} +pub fn thread_call( + thread_fd: usize, + payload: &mut [u8], + flags: CallFlags, + metadata: &[u64], +) -> Result { + sys_call(thread_fd, payload, flags, metadata) +} +pub fn this_thread_call(payload: &mut [u8], flags: CallFlags, metadata: &[u64]) -> Result { + thread_call(**RtTcb::current().thread_fd(), payload, flags, metadata) +} + +#[derive(Clone, Copy, Debug)] +pub enum WaitpidTarget { + AnyChild, + AnyGroupMember, + SingleProc { pid: usize }, + ProcGroup { pgid: usize }, +} +impl WaitpidTarget { + pub fn from_posix_arg(raw: isize) -> Self { + match raw { + 0 => Self::AnyGroupMember, + -1 => Self::AnyChild, + 1.. => Self::SingleProc { pid: raw as usize }, + ..-1 => Self::ProcGroup { + pgid: -raw as usize, + }, + } + } +} + +pub fn sys_waitpid(target: WaitpidTarget, status: &mut usize, flags: WaitFlags) -> Result { + let (call, pid) = match target { + WaitpidTarget::AnyChild => (ProcCall::Waitpid, 0), + WaitpidTarget::SingleProc { pid } => (ProcCall::Waitpid, pid), + WaitpidTarget::AnyGroupMember => (ProcCall::Waitpgid, 0), + WaitpidTarget::ProcGroup { pgid } => (ProcCall::Waitpgid, pgid), + }; + wrapper(true, false, || { + this_proc_call( + unsafe { plain::as_mut_bytes(status) }, + CallFlags::empty(), + &[call as u64, pid as u64, flags.bits() as u64], ) }) } pub fn posix_kill_thread(thread_fd: usize, signal: u32) -> Result<()> { - let killfd = FdGuard::new(syscall::dup(thread_fd, b"signal")?); - match wrapper(false, || syscall::write(*killfd, &signal.to_ne_bytes())) { - Ok(_) | Err(Error { errno: EINTR }) => Ok(()), + match wrapper(false, true, || { + thread_call( + thread_fd, + &mut [], + CallFlags::empty(), + &[ThreadCall::SignalThread as u64, signal.into()], + ) + }) { + Ok(_) | Err(Error { errno: ERESTART }) => Ok(()), Err(error) => Err(error), } } @@ -134,3 +224,126 @@ pub fn swap_umask(mask: u32) -> u32 { pub fn get_umask() -> u32 { UMASK.load(Ordering::Acquire) } + +/// Real/Effective/Set-User/Group ID +pub struct Resugid { + pub ruid: T, + pub euid: T, + pub suid: T, + pub rgid: T, + pub egid: T, + pub sgid: T, +} + +/// Sets [res][ug]id, fields that are None will be unchanged. +pub fn posix_setresugid(ids: &Resugid>) -> Result<()> { + // TODO: not sure how "tmp" an IPC call is? + let _sig_guard = tmp_disable_signals(); + let mut guard = DYNAMIC_PROC_INFO.lock(); + + let mut buf = [0_u8; size_of::() * 6]; + plain::slice_from_mut_bytes(&mut buf) + .unwrap() + .copy_from_slice(&[ + ids.ruid.unwrap_or(u32::MAX), + ids.euid.unwrap_or(u32::MAX), + ids.suid.unwrap_or(u32::MAX), + ids.rgid.unwrap_or(u32::MAX), + ids.egid.unwrap_or(u32::MAX), + ids.sgid.unwrap_or(u32::MAX), + ]); + + this_proc_call(&mut buf, CallFlags::empty(), &[ProcCall::SetResugid as u64])?; + + if let Some(ruid) = ids.ruid { + guard.ruid = ruid; + } + if let Some(euid) = ids.euid { + guard.euid = euid; + } + if let Some(suid) = ids.suid { + guard.suid = suid; + } + if let Some(rgid) = ids.rgid { + guard.rgid = rgid; + } + if let Some(egid) = ids.egid { + guard.egid = egid; + } + if let Some(sgid) = ids.sgid { + guard.sgid = sgid; + } + + Ok(()) +} +pub fn posix_getresugid() -> Resugid { + let _sig_guard = tmp_disable_signals(); + let DynamicProcInfo { + ruid, + euid, + suid, + rgid, + egid, + sgid, + .. + } = *DYNAMIC_PROC_INFO.lock(); + Resugid { + ruid, + euid, + suid, + rgid, + egid, + sgid, + } +} +pub fn posix_exit(status: i32) -> ! { + this_proc_call( + &mut [], + CallFlags::empty(), + &[ProcCall::Exit as u64, status as u64], + ) + .expect("failed to call proc mgr with Exit"); + let _ = syscall::write(1, b"redox-rt: ProcCall::Exit FAILED, abort()ing!\n"); + core::intrinsics::abort(); +} +pub fn setrens(rns: usize, ens: usize) -> Result<()> { + this_proc_call( + &mut [], + CallFlags::empty(), + &[ProcCall::Setrens as u64, rns as u64, ens as u64], + )?; + Ok(()) +} +pub fn posix_getpgid(pid: usize) -> Result { + this_proc_call( + &mut [], + CallFlags::empty(), + &[ProcCall::Setpgid as u64, pid as u64, u64::wrapping_neg(1)], + ) +} +pub fn posix_setpgid(pid: usize, pgid: usize) -> Result<()> { + if pgid == usize::wrapping_neg(1) { + return Err(Error::new(EINVAL)); + } + this_proc_call( + &mut [], + CallFlags::empty(), + &[ProcCall::Setpgid as u64, pid as u64, pgid as u64], + )?; + Ok(()) +} +pub fn posix_getsid(pid: usize) -> Result { + this_proc_call( + &mut [], + CallFlags::empty(), + &[ProcCall::Getsid as u64, pid as u64], + ) +} +pub fn posix_setsid() -> Result<()> { + this_proc_call(&mut [], CallFlags::empty(), &[ProcCall::Setsid as u64])?; + Ok(()) +} +pub fn posix_nanosleep(rqtp: &TimeSpec, rmtp: &mut TimeSpec) -> Result<()> { + wrapper(false, false, || syscall::nanosleep(rqtp, rmtp))?; + Ok(()) +} diff --git a/redox-rt/src/thread.rs b/redox-rt/src/thread.rs index 1791732a22..fb57f27410 100644 --- a/redox-rt/src/thread.rs +++ b/redox-rt/src/thread.rs @@ -1,18 +1,17 @@ use core::mem::size_of; -use syscall::{Result, O_CLOEXEC}; +use syscall::Result; -use crate::{arch::*, proc::*, signal::tmp_disable_signals, RtTcb}; +use crate::{arch::*, proc::*, signal::tmp_disable_signals, static_proc_info, RtTcb}; /// Spawns a new context sharing the same address space as the current one (i.e. a new thread). pub unsafe fn rlct_clone_impl(stack: *mut usize) -> Result { - let cur_thr_fd = RtTcb::current().thread_fd(); - let new_thr_fd = FdGuard::new(syscall::open( - "/scheme/thisproc/new-thread/open_via_dup", - O_CLOEXEC, - )?); + let proc_info = static_proc_info(); + assert!(proc_info.has_proc_fd); + let cur_proc_fd = proc_info.proc_fd.assume_init_ref(); - copy_str(**cur_thr_fd, *new_thr_fd, "name")?; + let cur_thr_fd = RtTcb::current().thread_fd(); + let new_thr_fd = FdGuard::new(syscall::dup(**cur_proc_fd, b"new-thread")?); // Inherit existing address space { @@ -54,16 +53,16 @@ pub unsafe fn exit_this_thread(stack_base: *mut (), stack_size: usize) -> ! { let _guard = tmp_disable_signals(); let tcb = RtTcb::current(); - let thread_fd = tcb.thread_fd(); + // TODO: modify interface so it writes directly to the thread fd? + let status_fd = syscall::dup(**tcb.thread_fd(), b"status").unwrap(); let _ = syscall::funmap(tcb as *const RtTcb as usize, syscall::PAGE_SIZE); - // TODO: modify interface so it writes directly to the thread fd? - let status_fd = syscall::dup(**thread_fd, b"status").unwrap(); let mut buf = [0; size_of::() * 3]; plain::slice_from_mut_bytes(&mut buf) .unwrap() .copy_from_slice(&[usize::MAX, stack_base as usize, stack_size]); + // TODO: SYS_CALL w/CONSUME syscall::write(status_fd, &buf).unwrap(); unreachable!() } diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs index 77189fc98b..925a584475 100644 --- a/src/header/signal/mod.rs +++ b/src/header/signal/mod.rs @@ -23,7 +23,7 @@ pub mod sys; #[path = "redox.rs"] pub mod sys; -type SigSet = BitSet<[c_ulong; 1]>; +type SigSet = BitSet<[u64; 1]>; pub(crate) const SIG_DFL: usize = 0; pub(crate) const SIG_IGN: usize = 1; @@ -309,7 +309,9 @@ pub unsafe extern "C" fn sigpause(sig: c_int) -> c_int { let mut pset = mem::MaybeUninit::::uninit(); sigprocmask(0, ptr::null_mut(), pset.as_mut_ptr()); let mut set = pset.assume_init(); - sigdelset(&mut set, sig); + if sigdelset(&mut set, sig) == -1 { + return -1; + } sigsuspend(&set) } diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs index d0140d3977..bcd682035e 100644 --- a/src/header/unistd/mod.rs +++ b/src/header/unistd/mod.rs @@ -569,15 +569,21 @@ pub extern "C" fn getppid() -> pid_t { } /// See . -// #[no_mangle] -pub extern "C" fn getresgid(rgid: *mut gid_t, egid: *mut gid_t, sgid: *mut gid_t) -> c_int { - unimplemented!(); +#[no_mangle] +pub unsafe extern "C" fn getresgid(rgid: *mut gid_t, egid: *mut gid_t, sgid: *mut gid_t) -> c_int { + // TODO: Out write-only wrapper? + Sys::getresgid(rgid.as_mut(), egid.as_mut(), sgid.as_mut()) + .map(|()| 0) + .or_minus_one_errno() } /// See . -// #[no_mangle] -pub extern "C" fn getresuid(ruid: *mut uid_t, euid: *mut uid_t, suid: *mut uid_t) -> c_int { - unimplemented!(); +#[no_mangle] +pub unsafe extern "C" fn getresuid(ruid: *mut uid_t, euid: *mut uid_t, suid: *mut uid_t) -> c_int { + // TODO: Out write-only wrapper? + Sys::getresuid(ruid.as_mut(), euid.as_mut(), suid.as_mut()) + .map(|()| 0) + .or_minus_one_errno() } /// See . @@ -812,13 +818,13 @@ pub unsafe extern "C" fn set_default_scheme(scheme: *const c_char) -> c_int { /// See . // #[no_mangle] pub extern "C" fn setegid(gid: gid_t) -> c_int { - unimplemented!(); + Sys::setresgid(-1, gid, -1).map(|()| 0).or_minus_one_errno() } /// See . // #[no_mangle] pub extern "C" fn seteuid(uid: uid_t) -> c_int { - unimplemented!(); + Sys::setresuid(-1, uid, -1).map(|()| 0).or_minus_one_errno() } /// See . diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index bf1df86639..ca55590d4f 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -612,10 +612,13 @@ impl Linker { unsafe { if !dlopened { #[cfg(target_os = "redox")] - let (tcb, old_tcb) = { + let (tcb, old_tcb, thr_fd) = { use redox_rt::signal::tmp_disable_signals; let old_tcb = Tcb::current().expect("failed to get bootstrap TCB"); + let thr_fd = (&mut *old_tcb.os_specific.thr_fd.get()) + .take() + .expect("no thread FD present"); let new_tcb = Tcb::new(self.tls_size)?; // This actually allocates TCB, TLS and ABI page. // Stash @@ -653,7 +656,7 @@ impl Linker { new_tcb.generic.tcb_len = new_tcb_len; drop(_guard); - (new_tcb, old_tcb as *mut Tcb as *mut c_void) + (new_tcb, old_tcb as *mut Tcb as *mut c_void, thr_fd) }; #[cfg(not(target_os = "redox"))] @@ -692,7 +695,10 @@ impl Linker { tcb.append_masters(tcb_masters); // Copy the master data into the static TLS block. tcb.copy_masters().map_err(|_| DlError::Malformed)?; - tcb.activate(); + tcb.activate( + #[cfg(target_os = "redox")] + thr_fd, + ); #[cfg(target_os = "redox")] { diff --git a/src/ld_so/mod.rs b/src/ld_so/mod.rs index eb3bb8c369..425d04d859 100644 --- a/src/ld_so/mod.rs +++ b/src/ld_so/mod.rs @@ -41,7 +41,10 @@ static mut STATIC_TCB_MASTER: Master = Master { }; #[inline(never)] -pub fn static_init(sp: &'static Stack) { +pub fn static_init( + sp: &'static Stack, + #[cfg(target_os = "redox")] thr_fd: redox_rt::proc::FdGuard, +) { const SIZEOF_PHDR64: usize = mem::size_of::>(); const SIZEOF_PHDR32: usize = mem::size_of::>(); @@ -120,7 +123,10 @@ pub fn static_init(sp: &'static Stack) { tcb.masters_len = mem::size_of::(); tcb.copy_masters() .expect_notls("failed to copy TLS master data"); - tcb.activate(); + tcb.activate( + #[cfg(target_os = "redox")] + thr_fd, + ); } //TODO: Warning on multiple TLS sections? @@ -130,7 +136,10 @@ pub fn static_init(sp: &'static Stack) { } #[cfg(any(target_os = "linux", target_os = "redox"))] -pub unsafe fn init(sp: &'static Stack) { +pub unsafe fn init( + sp: &'static Stack, + #[cfg(target_os = "redox")] thr_fd: redox_rt::proc::FdGuard, +) { let tp: usize; #[cfg(target_os = "linux")] @@ -151,11 +160,8 @@ pub unsafe fn init(sp: &'static Stack) { { let mut env = syscall::EnvRegisters::default(); - let file = syscall::open( - "/scheme/thisproc/current/regs/env", - syscall::O_CLOEXEC | syscall::O_RDONLY, - ) - .expect_notls("failed to open handle for process registers"); + let file = syscall::dup(*thr_fd, b"regs/env") + .expect_notls("failed to open handle for process registers"); let _ = syscall::read(file, &mut env).expect_notls("failed to read gsbase"); @@ -167,11 +173,8 @@ pub unsafe fn init(sp: &'static Stack) { { let mut env = syscall::EnvRegisters::default(); - let file = syscall::open( - "/scheme/thisproc/current/regs/env", - syscall::O_CLOEXEC | syscall::O_RDONLY, - ) - .expect_notls("failed to open handle for process registers"); + let file = syscall::dup(*thr_fd, b"regs/env") + .expect_notls("failed to open handle for process registers"); let _ = syscall::read(file, &mut env).expect_notls("failed to read fsbase"); @@ -188,7 +191,15 @@ pub unsafe fn init(sp: &'static Stack) { } if tp == 0 { - static_init(sp); + static_init( + sp, + #[cfg(target_os = "redox")] + thr_fd, + ); + } else { + // The thread fd must already be present in the already existing TCB. Don't close it. + #[cfg(target_os = "redox")] + core::mem::forget(thr_fd); } } diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index 5f40a6ef4e..26e1a0f3a3 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -145,16 +145,31 @@ fn resolve_path_name( None } -// TODO: Make unsafe #[no_mangle] -pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) -> usize { +pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) -> usize { // Setup TCB for ourselves. unsafe { - let tcb = Tcb::new(0).expect_notls("ld.so: failed to allocate bootstrap TCB"); - tcb.activate(); - #[cfg(target_os = "redox")] - redox_rt::signal::setup_sighandler(&tcb.os_specific); + let thr_fd = + crate::platform::get_auxv_raw(sp.auxv().cast(), redox_rt::auxv_defs::AT_REDOX_THR_FD) + .expect_notls("no thread fd present"); + + let tcb = Tcb::new(0).expect_notls("ld.so: failed to allocate bootstrap TCB"); + tcb.activate( + #[cfg(target_os = "redox")] + redox_rt::proc::FdGuard::new(thr_fd), + ); + #[cfg(target_os = "redox")] + { + let proc_fd = crate::platform::get_auxv_raw( + sp.auxv().cast(), + redox_rt::auxv_defs::AT_REDOX_PROC_FD, + ) + .expect_notls("no proc fd present"); + + redox_rt::initialize(redox_rt::proc::FdGuard::new(proc_fd)); + redox_rt::signal::setup_sighandler(&tcb.os_specific, true); + } } // We get the arguments, the environment, and the auxilary vector diff --git a/src/ld_so/tcb.rs b/src/ld_so/tcb.rs index 66fa39fb61..1191b7c1b3 100644 --- a/src/ld_so/tcb.rs +++ b/src/ld_so/tcb.rs @@ -202,8 +202,14 @@ impl Tcb { } /// Activate TLS - pub unsafe fn activate(&mut self) { - Self::os_arch_activate(&self.os_specific, self.tls_end as usize, self.tls_len); + pub unsafe fn activate(&mut self, #[cfg(target_os = "redox")] thr_fd: redox_rt::proc::FdGuard) { + Self::os_arch_activate( + &self.os_specific, + self.tls_end as usize, + self.tls_len, + #[cfg(target_os = "redox")] + thr_fd, + ); } pub fn setup_dtv(&mut self, n: usize) { @@ -327,7 +333,13 @@ impl Tcb { } #[cfg(target_os = "redox")] - unsafe fn os_arch_activate(os: &OsSpecific, tls_end: usize, tls_len: usize) { + unsafe fn os_arch_activate( + os: &OsSpecific, + tls_end: usize, + tls_len: usize, + thr_fd: redox_rt::proc::FdGuard, + ) { + os.thr_fd.get().write(Some(thr_fd)); redox_rt::tcb_activate(os, tls_end, tls_len) } } diff --git a/src/platform/auxv_defs.rs b/src/platform/auxv_defs.rs index ae9a6d36a1..5f6dad136b 100644 --- a/src/platform/auxv_defs.rs +++ b/src/platform/auxv_defs.rs @@ -26,6 +26,9 @@ pub const AT_RANDOM: usize = 25; /* Address of 16 random bytes. */ pub const AT_HWCAP2: usize = 26; /* More machine-dependent hints about*/ pub const AT_EXECFN: usize = 31; /* Filename of executable. */ +// TODO: Downgrade aux vectors to getauxval constants on Redox, and use a regular struct for +// passing important runtime info between exec (or posix_spawn in the future) calls. + #[cfg(target_os = "redox")] // XXX: The name AT_CWD is already used in openat... for a completely different purpose. pub const AT_REDOX_INITIAL_CWD_PTR: usize = 32; @@ -48,3 +51,9 @@ pub const AT_REDOX_INITIAL_DEFAULT_SCHEME_LEN: usize = 39; #[cfg(target_os = "redox")] pub const AT_REDOX_UMASK: usize = 40; + +#[cfg(target_os = "redox")] +pub const AT_REDOX_PROC_FD: usize = 41; + +#[cfg(target_os = "redox")] +pub const AT_REDOX_THR_FD: usize = 42; diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 86ae1af3b7..b624a7da3a 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -328,6 +328,37 @@ impl Pal for Sys { e_raw(syscall!(GETRLIMIT, resource, rlim)).map(|_| ()) } + fn getresgid( + rgid: Option<&mut gid_t>, + egid: Option<&mut gid_t>, + sgid: Option<&mut gid_t>, + ) -> Result<()> { + unsafe { + e_raw(syscall!( + GETRESGID, + rgid.map_or(0, |r| r as *const _ as usize), + egid.map_or(0, |r| r as *const _ as usize), + sgid.map_or(0, |r| r as *const _ as usize) + )) + .map(|_| ()) + } + } + fn getresuid( + ruid: Option<&mut uid_t>, + euid: Option<&mut uid_t>, + suid: Option<&mut uid_t>, + ) -> Result<()> { + unsafe { + e_raw(syscall!( + GETRESUID, + ruid.map_or(0, |r| r as *const _ as usize), + euid.map_or(0, |r| r as *const _ as usize), + suid.map_or(0, |r| r as *const _ as usize) + )) + .map(|_| ()) + } + } + unsafe fn setrlimit(resource: c_int, rlimit: *const rlimit) -> Result<()> { e_raw(syscall!(SETRLIMIT, resource, rlimit)).map(|_| ()) } diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 74b2519291..588c526951 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -280,21 +280,40 @@ impl Write for CountingWriter { // get_auxv. #[cold] -pub unsafe fn get_auxvs(mut ptr: *const usize) -> Box<[[usize; 2]]> { - //traverse the stack and collect argument environment variables - let mut auxvs = Vec::new(); +unsafe fn auxv_iter<'a>(ptr: *const usize) -> impl Iterator + 'a { + struct St(*const usize); + impl Iterator for St { + type Item = [usize; 2]; - while *ptr != self::auxv_defs::AT_NULL { - let kind = ptr.read(); - ptr = ptr.add(1); - let value = ptr.read(); - ptr = ptr.add(1); - auxvs.push([kind, value]); + fn next(&mut self) -> Option { + unsafe { + if self.0.read() == self::auxv_defs::AT_NULL { + return None; + } + let kind = self.0.read(); + let value = self.0.add(1).read(); + self.0 = self.0.add(2); + + Some([kind, value]) + } + } } + St(ptr) +} + +#[cold] +pub unsafe fn get_auxvs(ptr: *const usize) -> Box<[[usize; 2]]> { + //traverse the stack and collect argument environment variables + let mut auxvs = auxv_iter(ptr).collect::>(); auxvs.sort_unstable_by_key(|[kind, _]| *kind); auxvs.into_boxed_slice() } +// TODO: Find an auxv replacement for Redox's execv protocol +#[cold] +pub unsafe fn get_auxv_raw(ptr: *const usize, requested_kind: usize) -> Option { + auxv_iter(ptr).find_map(|[kind, value]| Some(value).filter(|_| kind == requested_kind)) +} pub fn get_auxv(auxvs: &[[usize; 2]], key: usize) -> Option { auxvs .binary_search_by_key(&key, |[entry_key, _]| *entry_key) @@ -306,13 +325,23 @@ pub fn get_auxv(auxvs: &[[usize; 2]], key: usize) -> Option { #[cfg(target_os = "redox")] // SAFETY: Must only be called when only one thread exists. pub unsafe fn init(auxvs: Box<[[usize; 2]]>) { - redox_rt::initialize(); - + use self::auxv_defs::*; + use crate::header::sys_stat::S_ISVTX; + use redox_rt::proc::FdGuard; use syscall::MODE_PERM; - use crate::header::sys_stat::S_ISVTX; + let Some(proc_fd) = get_auxv(&auxvs, AT_REDOX_PROC_FD) else { + panic!("Missing proc and thread fd!"); + }; + redox_rt::initialize(FdGuard::new(proc_fd)); - use self::auxv_defs::*; + // TODO: Is it safe to assume setup_sighandler has been called at this point? + redox_rt::sys::this_proc_call( + &mut [], + syscall::CallFlags::empty(), + &[redox_rt::protocol::ProcCall::SyncSigPctl as u64], + ) + .expect("failed to sync signal pctl"); if let (Some(cwd_ptr), Some(cwd_len)) = ( get_auxv(&auxvs, AT_REDOX_INITIAL_CWD_PTR), diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index 6c55a98955..fd5de3e2d8 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -131,6 +131,18 @@ pub trait Pal { fn getrandom(buf: &mut [u8], flags: c_uint) -> Result; + fn getresgid( + rgid: Option<&mut gid_t>, + egid: Option<&mut gid_t>, + sgid: Option<&mut gid_t>, + ) -> Result<()>; + + fn getresuid( + ruid: Option<&mut uid_t>, + euid: Option<&mut uid_t>, + suid: Option<&mut uid_t>, + ) -> Result<()>; + unsafe fn getrlimit(resource: c_int, rlim: *mut rlimit) -> Result<()>; unsafe fn setrlimit(resource: c_int, rlim: *const rlimit) -> Result<()>; diff --git a/src/platform/redox/clone.rs b/src/platform/redox/clone.rs index 3c78230fa7..e382089394 100644 --- a/src/platform/redox/clone.rs +++ b/src/platform/redox/clone.rs @@ -4,7 +4,7 @@ use syscall::{ data::Map, error::Result, flag::{MapFlags, O_CLOEXEC}, - SetSighandlerData, SIGCONT, + SetSighandlerData, }; use redox_rt::{proc::FdGuard, signal::sighandler_function}; diff --git a/src/platform/redox/exec.rs b/src/platform/redox/exec.rs index 10e09eccc1..c7f9aed4b9 100644 --- a/src/platform/redox/exec.rs +++ b/src/platform/redox/exec.rs @@ -14,12 +14,15 @@ use crate::{ }, }; -use redox_rt::proc::{ExtraInfo, FdGuard, FexecResult, InterpOverride}; +use redox_rt::{ + proc::{ExtraInfo, FdGuard, FexecResult, InterpOverride}, + sys::Resugid, + RtTcb, +}; use syscall::{data::Stat, error::*, flag::*}; fn fexec_impl( exec_file: FdGuard, - open_via_dup: FdGuard, path: &[u8], args: &[&[u8]], envs: &[&[u8]], @@ -31,7 +34,8 @@ fn fexec_impl( let addrspace_selection_fd = match redox_rt::proc::fexec_impl( exec_file, - open_via_dup, + &RtTcb::current().thread_fd(), + redox_rt::current_proc_fd(), &memory, path, args.iter().rev(), @@ -43,12 +47,10 @@ fn fexec_impl( FexecResult::Normal { addrspace_handle } => addrspace_handle, FexecResult::Interp { image_file, - open_via_dup, path, interp_override: new_interp_override, } => { drop(image_file); - drop(open_via_dup); drop(memory); // According to elf(5), PT_INTERP requires that the interpreter path be @@ -119,12 +121,11 @@ pub fn execve( let mut stat = Stat::default(); syscall::fstat(*image_file as usize, &mut stat)?; - let uid = syscall::getuid()?; - let gid = syscall::getuid()?; + let Resugid { ruid, rgid, .. } = redox_rt::sys::posix_getresugid(); - let mode = if uid == stat.st_uid as usize { + let mode = if ruid == stat.st_uid { (stat.st_mode >> 3 * 2) & 0o7 - } else if gid == stat.st_gid as usize { + } else if rgid == stat.st_gid { (stat.st_mode >> 3 * 1) & 0o7 } else { stat.st_mode & 0o7 @@ -243,7 +244,7 @@ pub fn execve( // threads, it could still be allowed by keeping certain file descriptors and instead // set the active file table. let files_fd = - File::new(syscall::open("/scheme/thisproc/current/filetable", O_RDONLY)? as c_int); + File::new(syscall::dup(**RtTcb::current().thread_fd(), b"filetable")? as c_int); for line in BufReader::new(files_fd).lines() { let line = match line { Ok(l) => l, @@ -262,7 +263,8 @@ pub fn execve( } } - let this_context_fd = FdGuard::new(syscall::open("/scheme/thisproc/current/open_via_dup", 0)?); + let this_thr_fd = RtTcb::current().thread_fd(); + // TODO: Convert image_file to FdGuard earlier? let exec_fd_guard = FdGuard::new(image_file.fd as usize); core::mem::forget(image_file); @@ -272,7 +274,10 @@ pub fn execve( let escalate_fd = FdGuard::new(syscall::open("/scheme/escalate", O_WRONLY)?); // First, send the context handle of this process to escalated. - send_fd_guard(*escalate_fd, this_context_fd)?; + send_fd_guard( + *escalate_fd, + FdGuard::new(syscall::dup(**this_thr_fd, &[])?), + )?; // Then, send the file descriptor containing the file descriptor to be executed. send_fd_guard(*escalate_fd, exec_fd_guard)?; @@ -314,10 +319,11 @@ pub fn execve( sigignmask: 0, sigprocmask, umask: redox_rt::sys::get_umask(), + thr_fd: **RtTcb::current().thread_fd(), + proc_fd: **redox_rt::current_proc_fd(), }; fexec_impl( exec_fd_guard, - this_context_fd, arg0, &args, &envs, diff --git a/src/platform/redox/libredox.rs b/src/platform/redox/libredox.rs index 5447db0436..5e5bcad345 100644 --- a/src/platform/redox/libredox.rs +++ b/src/platform/redox/libredox.rs @@ -1,7 +1,10 @@ use core::{slice, str}; -use redox_rt::sys::{posix_read, posix_write}; -use syscall::{Error, Result, WaitFlags, EMFILE}; +use redox_rt::{ + protocol::{ProcKillTarget, WaitFlags}, + sys::{posix_read, posix_write, WaitpidTarget}, +}; +use syscall::{Error, Result, EMFILE}; use crate::{ header::{ @@ -219,34 +222,34 @@ pub unsafe extern "C" fn redox_close_v1(fd: usize) -> RawResult { #[no_mangle] pub unsafe extern "C" fn redox_get_pid_v1() -> RawResult { - Error::mux(syscall::getpid()) + redox_rt::sys::posix_getpid() as _ } #[no_mangle] pub unsafe extern "C" fn redox_get_euid_v1() -> RawResult { - Error::mux(syscall::geteuid()) + redox_rt::sys::posix_getresugid().euid as _ } #[no_mangle] pub unsafe extern "C" fn redox_get_ruid_v1() -> RawResult { - Error::mux(syscall::getuid()) + redox_rt::sys::posix_getresugid().ruid as _ } #[no_mangle] pub unsafe extern "C" fn redox_get_egid_v1() -> RawResult { - Error::mux(syscall::getegid()) + redox_rt::sys::posix_getresugid().egid as _ } #[no_mangle] pub unsafe extern "C" fn redox_get_rgid_v1() -> RawResult { - Error::mux(syscall::getgid()) + redox_rt::sys::posix_getresugid().rgid as _ } #[no_mangle] pub unsafe extern "C" fn redox_setrens_v1(rns: usize, ens: usize) -> RawResult { - Error::mux(syscall::setrens(rns, ens)) + Error::mux(redox_rt::sys::setrens(rns, ens).map(|()| 0)) } #[no_mangle] pub unsafe extern "C" fn redox_waitpid_v1(pid: usize, status: *mut i32, options: u32) -> RawResult { let mut sts = 0_usize; - let res = Error::mux(syscall::waitpid( - pid, + let res = Error::mux(redox_rt::sys::sys_waitpid( + WaitpidTarget::from_posix_arg(pid as isize), &mut sts, WaitFlags::from_bits_truncate(options as usize), )); @@ -256,7 +259,9 @@ pub unsafe extern "C" fn redox_waitpid_v1(pid: usize, status: *mut i32, options: #[no_mangle] pub unsafe extern "C" fn redox_kill_v1(pid: usize, signal: u32) -> RawResult { - Error::mux(syscall::kill(pid, signal as usize)) + Error::mux( + redox_rt::sys::posix_kill(ProcKillTarget::from_raw(pid), signal as usize).map(|()| 0), + ) } #[no_mangle] @@ -360,3 +365,9 @@ pub unsafe extern "C" fn redox_mkns_v1( syscall::mkns(core::slice::from_raw_parts(names.cast(), num_names)) })()) } + +// ABI-UNSTABLE +#[no_mangle] +pub unsafe extern "C" fn redox_cur_thrfd_v0() -> usize { + **redox_rt::RtTcb::current().thread_fd() +} diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index f3cfa0b137..3c9b273b97 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -3,7 +3,11 @@ use core::{ mem::{self, size_of}, ptr, slice, str, }; -use redox_rt::RtTcb; +use redox_rt::{ + protocol::{wifstopped, wstopsig, WaitFlags}, + sys::{Resugid, WaitpidTarget}, + RtTcb, +}; use syscall::{ self, data::{Map, Stat as redox_stat, StatVfs as redox_statvfs, TimeSpec as redox_timespec}, @@ -50,6 +54,13 @@ fn round_up_to_page_size(val: usize) -> Option { .map(|val| (val - 1) / PAGE_SIZE * PAGE_SIZE) } +fn cvt_uid(id: c_int) -> Result> { + if id == -1 { + return Ok(None); + } + Ok(Some(id.try_into().map_err(|_| Errno(EINVAL))?)) +} + mod clone; mod epoll; mod event; @@ -93,12 +104,11 @@ impl Pal for Sys { syscall::fstat(*fd as usize, &mut stat)?; - let uid = syscall::getuid()?; - let gid = syscall::getgid()?; + let Resugid { ruid, rgid, .. } = redox_rt::sys::posix_getresugid(); - let perms = if stat.st_uid as usize == uid { + let perms = if stat.st_uid == ruid { stat.st_mode >> (3 * 2 & 0o7) - } else if stat.st_gid as usize == gid { + } else if stat.st_gid == rgid { stat.st_mode >> (3 * 1 & 0o7) } else { stat.st_mode & 0o7 @@ -199,7 +209,7 @@ impl Pal for Sys { } fn exit(status: c_int) -> ! { - let _ = syscall::exit((status as usize) << 8); + let _ = redox_rt::sys::posix_exit(status); loop {} } @@ -265,7 +275,7 @@ impl Pal for Sys { // TODO: Find way to avoid lock. let _guard = CLONE_LOCK.write(); - Ok(clone::fork_impl()? as pid_t) + Ok(clone::fork_impl(&redox_rt::proc::ForkArgs::Managed)? as pid_t) } unsafe fn fstat(fildes: c_int, buf: *mut stat) -> Result<()> { @@ -401,15 +411,15 @@ impl Pal for Sys { } fn getegid() -> gid_t { - syscall::getegid().unwrap() as gid_t + redox_rt::sys::posix_getresugid().egid as gid_t } fn geteuid() -> uid_t { - syscall::geteuid().unwrap() as uid_t + redox_rt::sys::posix_getresugid().euid as uid_t } fn getgid() -> gid_t { - syscall::getgid().unwrap() as gid_t + redox_rt::sys::posix_getresugid().rgid as gid_t } unsafe fn getgroups(size: c_int, list: *mut gid_t) -> Result { @@ -423,7 +433,7 @@ impl Pal for Sys { } fn getpgid(pid: pid_t) -> Result { - Ok(syscall::getpgid(pid as usize)? as pid_t) + Ok(redox_rt::sys::posix_getpgid(pid as usize)? as pid_t) } fn getpid() -> pid_t { @@ -431,7 +441,7 @@ impl Pal for Sys { } fn getppid() -> pid_t { - syscall::getppid().unwrap() as pid_t + redox_rt::sys::posix_getppid() as pid_t } fn getpriority(which: c_int, who: id_t) -> Result { @@ -458,6 +468,45 @@ impl Pal for Sys { Ok(syscall::read(*fd, buf)?) } + fn getresgid( + rgid_out: Option<&mut gid_t>, + egid_out: Option<&mut gid_t>, + sgid_out: Option<&mut gid_t>, + ) -> Result<()> { + let Resugid { + rgid, egid, sgid, .. + } = redox_rt::sys::posix_getresugid(); + if let Some(rgid_out) = rgid_out { + *rgid_out = rgid as _; + } + if let Some(egid_out) = egid_out { + *egid_out = egid as _; + } + if let Some(sgid_out) = sgid_out { + *sgid_out = sgid as _; + } + Ok(()) + } + fn getresuid( + ruid_out: Option<&mut uid_t>, + euid_out: Option<&mut uid_t>, + suid_out: Option<&mut uid_t>, + ) -> Result<()> { + let Resugid { + ruid, euid, suid, .. + } = redox_rt::sys::posix_getresugid(); + if let Some(ruid_out) = ruid_out { + *ruid_out = ruid as _; + } + if let Some(euid_out) = euid_out { + *euid_out = euid as _; + } + if let Some(suid_out) = suid_out { + *suid_out = suid as _; + } + Ok(()) + } + unsafe fn getrlimit(resource: c_int, rlim: *mut rlimit) -> Result<()> { //TODO eprintln!( @@ -487,17 +536,7 @@ impl Pal for Sys { } fn getsid(pid: pid_t) -> Result { - let mut buf = [0; mem::size_of::()]; - let path = if pid == 0 { - format!("/scheme/thisproc/current/session_id") - } else { - format!("/scheme/proc/{}/session_id", pid) - }; - let path_c = CString::new(path).unwrap(); - let mut file = File::open(CStr::borrow(&path_c), fcntl::O_RDONLY | fcntl::O_CLOEXEC)?; - file.read(&mut buf) - .map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))?; - Ok(usize::from_ne_bytes(buf).try_into().unwrap()) + Ok(redox_rt::sys::posix_getsid(pid as usize)? as _) } fn gettid() -> pid_t { @@ -527,7 +566,7 @@ impl Pal for Sys { } fn getuid() -> uid_t { - syscall::getuid().unwrap() as pid_t + redox_rt::sys::posix_getresugid().ruid as uid_t } fn lchown(path: CStr, owner: uid_t, group: gid_t) -> Result<()> { @@ -703,7 +742,7 @@ impl Pal for Sys { } else { redox_rmtp = unsafe { redox_timespec::from(&*rmtp) }; } - match syscall::nanosleep(&redox_rqtp, &mut redox_rmtp) { + match redox_rt::sys::posix_nanosleep(&redox_rqtp, &mut redox_rmtp) { Ok(_) => Ok(()), Err(Error { errno: EINTR }) => { unsafe { @@ -838,7 +877,7 @@ impl Pal for Sys { } fn setpgid(pid: pid_t, pgid: pid_t) -> Result<()> { - syscall::setpgid(pid as usize, pgid as usize)?; + redox_rt::sys::posix_setpgid(pid as usize, pgid as usize)?; Ok(()) } @@ -852,30 +891,31 @@ impl Pal for Sys { } fn setsid() -> Result<()> { - let session_id = Self::getpid(); - assert!(session_id >= 0); - let mut file = File::open( - c"/scheme/thisproc/current/session_id".into(), - fcntl::O_WRONLY | fcntl::O_CLOEXEC, - )?; - file.write(&usize::to_ne_bytes(session_id.try_into().unwrap())) - .map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))?; + redox_rt::sys::posix_setsid()?; Ok(()) } fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> Result<()> { - if sgid != -1 { - println!("TODO: suid"); - } - syscall::setregid(rgid as usize, egid as usize)?; + redox_rt::sys::posix_setresugid(&Resugid { + ruid: None, + euid: None, + suid: None, + rgid: cvt_uid(rgid)?, + egid: cvt_uid(egid)?, + sgid: cvt_uid(sgid)?, + })?; Ok(()) } fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> Result<()> { - if suid != -1 { - println!("TODO: suid"); - } - syscall::setreuid(ruid as usize, euid as usize)?; + redox_rt::sys::posix_setresugid(&Resugid { + ruid: cvt_uid(ruid)?, + euid: cvt_uid(euid)?, + suid: cvt_uid(suid)?, + rgid: None, + egid: None, + sgid: None, + })?; Ok(()) } @@ -981,14 +1021,16 @@ impl Pal for Sys { } unsafe fn waitpid(mut pid: pid_t, stat_loc: *mut c_int, options: c_int) -> Result { - if pid == !0 { - pid = 0; - } let mut res = None; let mut status = 0; + let options = usize::try_from(options) + .ok() + .and_then(WaitFlags::from_bits) + .ok_or(Errno(EINVAL))?; + let inner = |status: &mut usize, flags| { - redox_rt::sys::sys_waitpid(pid as usize, status, flags as usize) + redox_rt::sys::sys_waitpid(WaitpidTarget::from_posix_arg(pid as isize), status, flags) }; // First, allow ptrace to handle waitpid @@ -996,19 +1038,19 @@ impl Pal for Sys { let state = ptrace::init_state(); let mut sessions = state.sessions.lock(); if let Ok(session) = ptrace::get_session(&mut sessions, pid) { - if options & sys_wait::WNOHANG != sys_wait::WNOHANG { + if !options.contains(WaitFlags::WNOHANG) { let mut _event = PtraceEvent::default(); let _ = (&mut &session.tracer).read(&mut _event); res = Some(inner( &mut status, - options | sys_wait::WNOHANG | sys_wait::WUNTRACED, + options | WaitFlags::WNOHANG | WaitFlags::WUNTRACED, )); if res == Some(Ok(0)) { // WNOHANG, just pretend ptrace SIGSTOP:ped this - status = (syscall::SIGSTOP << 8) | 0x7f; - assert!(syscall::wifstopped(status)); - assert_eq!(syscall::wstopsig(status), syscall::SIGSTOP); + status = (redox_rt::protocol::SIGSTOP << 8) | 0x7f; + assert!(wifstopped(status)); + assert_eq!(wstopsig(status), redox_rt::protocol::SIGSTOP); res = Some(Ok(pid as usize)); } } @@ -1019,11 +1061,11 @@ impl Pal for Sys { // it if (and only if) a ptrace traceme was activated during // the wait. let res = res.unwrap_or_else(|| loop { - let res = inner(&mut status, options | sys_wait::WUNTRACED); + let res = inner(&mut status, options | WaitFlags::WUNTRACED); // TODO: Also handle special PIDs here - if !syscall::wifstopped(status) - || options & sys_wait::WUNTRACED != 0 + if !wifstopped(status) + || options.contains(WaitFlags::WUNTRACED) || ptrace::is_traceme(pid) { break res; @@ -1057,8 +1099,8 @@ impl Pal for Sys { } fn verify() -> bool { - // GETPID on Redox is 20, which is WRITEV on Linux - (unsafe { syscall::syscall5(syscall::number::SYS_GETPID, !0, !0, !0, !0, !0) }).is_ok() + // YIELD on Redox is 20, which is SYS_ARCH_PRCTL on Linux + (unsafe { syscall::syscall5(syscall::number::SYS_YIELD, !0, !0, !0, !0, !0) }).is_ok() } unsafe fn exit_thread(stack_base: *mut (), stack_size: usize) -> ! { diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs index e4a16c9443..dac99ef3b7 100644 --- a/src/platform/redox/signal.rs +++ b/src/platform/redox/signal.rs @@ -18,6 +18,7 @@ use crate::{ use core::mem::{self, offset_of}; use redox_rt::{ proc::FdGuard, + protocol::ProcKillTarget, signal::{ PosixStackt, SigStack, Sigaction, SigactionFlags, SigactionKind, Sigaltstack, SignalHandler, }, @@ -65,7 +66,7 @@ impl PalSignal for Sys { } fn kill(pid: pid_t, sig: c_int) -> Result<()> { - redox_rt::sys::posix_kill(pid as usize, sig as usize)?; + redox_rt::sys::posix_kill(ProcKillTarget::from_raw(pid as usize), sig as usize)?; Ok(()) } fn sigqueue(pid: pid_t, sig: c_int, val: sigval) -> Result<()> { @@ -77,8 +78,10 @@ impl PalSignal for Sys { } fn killpg(pgrp: pid_t, sig: c_int) -> Result<()> { - redox_rt::sys::posix_killpg(pgrp as usize, sig as usize)?; - Ok(()) + if pgrp == 1 { + return Err(Errno(EINVAL)); + } + Self::kill(-pgrp, sig) } fn raise(sig: c_int) -> Result<()> { diff --git a/src/pthread/mod.rs b/src/pthread/mod.rs index d7b97fe1de..bdf70af6ba 100644 --- a/src/pthread/mod.rs +++ b/src/pthread/mod.rs @@ -210,19 +210,21 @@ unsafe extern "C" fn new_thread_shim( mutex1: *const Mutex>, mutex2: *const Mutex, ) -> ! { + let tid = (*(&*mutex1).lock()).assume_init(); + if let Some(tcb) = tcb.as_mut() { - tcb.activate(); + #[cfg(not(target_os = "redox"))] + { + tcb.activate(); + } + #[cfg(target_os = "redox")] + { + tcb.activate(redox_rt::proc::FdGuard::new(tid.thread_fd)); + redox_rt::signal::setup_sighandler(&tcb.os_specific, false); + } } let procmask = (&*mutex2).as_ptr().read(); - let tid = (*(&*mutex1).lock()).assume_init(); - - #[cfg(target_os = "redox")] - (*tcb) - .os_specific - .thr_fd - .get() - .write(Some(redox_rt::proc::FdGuard::new(tid.thread_fd))); if let Some(tcb) = tcb.as_mut() { tcb.copy_masters().unwrap(); diff --git a/src/start.rs b/src/start.rs index 3d56738632..279dcbe317 100644 --- a/src/start.rs +++ b/src/start.rs @@ -2,6 +2,7 @@ use alloc::{boxed::Box, vec::Vec}; use core::{intrinsics, ptr}; +use generic_rt::ExpectTlsFree; use crate::{ header::{libgen, stdio, stdlib}, @@ -141,8 +142,18 @@ pub unsafe extern "C" fn relibc_start(sp: &'static Stack) -> ! { // Ensure correct host system before executing more system calls relibc_verify_host(); + #[cfg(target_os = "redox")] + let thr_fd = redox_rt::proc::FdGuard::new( + crate::platform::get_auxv_raw(sp.auxv().cast(), redox_rt::auxv_defs::AT_REDOX_THR_FD) + .expect_notls("no thread fd present"), + ); + // Initialize TLS, if necessary - ld_so::init(sp); + ld_so::init( + sp, + #[cfg(target_os = "redox")] + thr_fd, + ); // Set up the right allocator... // if any memory rust based memory allocation happen before this step .. we are doomed. @@ -160,7 +171,7 @@ pub unsafe extern "C" fn relibc_start(sp: &'static Stack) -> ! { tcb.linker_ptr = Box::into_raw(Box::new(Mutex::new(linker))); } #[cfg(target_os = "redox")] - redox_rt::signal::setup_sighandler(&tcb.os_specific); + redox_rt::signal::setup_sighandler(&tcb.os_specific, true); } // Set up argc and argv diff --git a/tests/Makefile b/tests/Makefile index 948215e198..38412a55ce 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -125,10 +125,12 @@ EXPECT_NAMES=\ unistd/getopt_long \ unistd/pipe \ unistd/rmdir \ + waitpid \ + waitpid_multiple \ + kill-waitpid \ # unistd/sleep \ unistd/swab \ unistd/write \ - waitpid \ wchar/fgetwc \ wchar/fwide \ wchar/mbrtowc \ @@ -204,6 +206,7 @@ NAMES=\ unistd/sysconf \ pthread/main \ pthread/cleanup \ + pthread/exit \ pthread/extjoin \ pthread/once \ pthread/customstack \ @@ -216,9 +219,9 @@ NAMES=\ grp/getgrgid_r \ grp/getgrnam_r \ grp/gr_iter \ + sigqueue # resource/getrusage # time/times -# sigqueue # Tests run with `expect` (require a .c file and an .exp file # that takes the produced binary as the first argument) diff --git a/tests/kill-waitpid.c b/tests/kill-waitpid.c new file mode 100644 index 0000000000..000029127c --- /dev/null +++ b/tests/kill-waitpid.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + int err; + + int fds[2]; + err = pipe(fds); + ERROR_IF(pipe, err, == -1); + + int child = fork(); + ERROR_IF(fork, child, == -1); + + if (child == 0) { + // block forever + char buf[1]; + read(fds[0], buf, 1); + } + + err = kill(child, SIGTERM); + ERROR_IF(kill, err, == -1); + + int status; + err = waitpid(child, &status, 0); + ERROR_IF(waitpid, err, == -1); + printf("STATUS %d\n", status); + assert(WIFSIGNALED(status)); + assert(WTERMSIG(status) == SIGTERM); +} diff --git a/tests/pthread/exit.c b/tests/pthread/exit.c new file mode 100644 index 0000000000..ce276b4239 --- /dev/null +++ b/tests/pthread/exit.c @@ -0,0 +1,31 @@ +#include +#include +#include + +#include + +#include "common.h" + +void *routine(void *arg) { + assert(arg == NULL); + + sleep(1); + + puts("Thread succeeded"); + + return NULL; +} + +int main(void) { + int status; + pthread_t thread; + + if ((status = pthread_create(&thread, NULL, routine, NULL)) != 0) { + return fail(status, "failed to create thread"); + } + if ((status = pthread_detach(thread)) != 0) { + return fail(status, "failed to detach thread"); + } + + pthread_exit(NULL); +} diff --git a/tests/waitpid.c b/tests/waitpid.c index ec4c6dedc6..09a8ffc537 100644 --- a/tests/waitpid.c +++ b/tests/waitpid.c @@ -1,21 +1,36 @@ +#include #include #include #include #include "test_helpers.h" -int main(void) { +void for_code(int code) { pid_t pid = fork(); ERROR_IF(fork, pid, == -1); + // Testing successful exit if (pid == 0) { // child sleep(1); - exit(EXIT_SUCCESS); - } else { - // parent - int stat_loc; - pid_t wid = waitpid(pid, &stat_loc, 0); - ERROR_IF(waitpid, wid, == -1); + _Exit(code); } + printf("Testing waitpid of child %d for code %d\n", pid, code); + // parent + int status; + pid_t wid = waitpid(pid, &status, 0); + ERROR_IF(waitpid, wid, == -1); + + assert(WIFEXITED(status)); + assert(WEXITSTATUS(status) == code); + + puts("Success"); +} + +int main(void) { + for_code(EXIT_SUCCESS); + for_code(EXIT_FAILURE); + for_code(42); + for_code(255); + // TODO: Also add coverage for e.g. WIFSTOPPED, WSTOPSIG, WTERMSIG, etc } diff --git a/tests/waitpid_multiple.c b/tests/waitpid_multiple.c new file mode 100644 index 0000000000..b872ed1fb5 --- /dev/null +++ b/tests/waitpid_multiple.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + // Spawn two children, one with same pgid and one with pgid set to its own + // pid, so that the one with different pgid completes first. Then test + // waitpid both for 'any child' and for 'any child with same pgid'. + + pid_t pid_samepgid = fork(); + ERROR_IF(fork, pid_samepgid, == -1); + + if (pid_samepgid == 0) { + // child + sleep(2); + _Exit(2); + } + pid_t pid_diffpgids[2]; + for (int i = 0; i < 2; i++) { + pid_diffpgids[i] = fork(); + ERROR_IF(fork, pid_diffpgids[i], == -1); + + if (pid_diffpgids[i] == 0) { + int ret = setpgid(0, 0); + ERROR_IF(setpgid, ret, == -1); + + // child + sleep(1); + _Exit(i); + } + } + int status; + pid_t wid; + + // First, check that the first different-pgid proc is recognized. + wid = waitpid(-1, &status, 0); + ERROR_IF(waitpid, wid, == -1); + assert(wid == pid_diffpgids[0]); + assert(WIFEXITED(status)); + assert(WEXITSTATUS(status) == 0); + + // Then, check that the longest-waiting proc with the same pgid is properly matched. + wid = waitpid(0, &status, 0); + ERROR_IF(waitpid, wid, == -1); + assert(wid == pid_samepgid); + assert(WIFEXITED(status)); + assert(WEXITSTATUS(status) == 2); + + // Finally, the last same-pgid must have completed. + wid = waitpid(-1, &status, WNOHANG); + ERROR_IF(waitpid, wid, == -1); + assert(wid == pid_diffpgids[1]); + assert(WIFEXITED(status)); + assert(WEXITSTATUS(status) == 1); +}