Merge branch 'userspace_proc' into 'master'

Use process manager

See merge request redox-os/relibc!649
This commit is contained in:
Jeremy Soller
2025-04-19 18:04:40 +00:00
37 changed files with 1649 additions and 438 deletions
Generated
+5 -5
View File
@@ -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",
]
+1 -1
View File
@@ -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" }
+7
View File
@@ -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).
[<img src="https://nlnet.nl/logo/banner.png" alt="NLnet foundation logo" width="20%" />](https://nlnet.nl)
[<img src="https://nlnet.nl/image/logos/NGI0_tag.svg" alt="NGI Zero Logo" width="20%" />](https://nlnet.nl/core)
+1 -1
View File
@@ -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)
+4
View File
@@ -15,3 +15,7 @@ plain = "0.2"
redox_syscall = "0.5.8"
generic-rt = { path = "../generic-rt" }
[features]
proc = []
default = ["proc"]
+63 -36
View File
@@ -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::<RtSigInfo>(),
]);
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<usize> = SyncUnsafeCell::new(usize::MAX);
static PROC_CALL: [usize; 1] = [ProcCall::Sigdeq as usize];
+66 -27
View File
@@ -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<NonNull<SigStack>>,
@@ -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::<RtSigInfo>(),
]);
asmfunction!(__relibc_internal_rlct_clone_ret -> usize: ["
@@ -377,3 +413,6 @@ pub fn current_sp() -> usize {
}
sp
}
pub static PROC_FD: SyncUnsafeCell<usize> = SyncUnsafeCell::new(usize::MAX);
static PROC_CALL: u64 = ProcCall::Sigdeq as u64;
+49 -13
View File
@@ -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::<RtSigInfo>(),
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<usize> = SyncUnsafeCell::new(usize::MAX);
static PROC_CALL: [usize; 1] = [ProcCall::Sigdeq as usize];
+62 -24
View File
@@ -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::<RtSigInfo>(),
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<usize> = SyncUnsafeCell::new(usize::MAX);
+136 -27
View File
@@ -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<ProcMeta> {
let mut bytes = [0_u8; size_of::<ProcMeta>()];
let _ = syscall::read(**proc, &mut bytes)?;
Ok(*plain::from_bytes::<ProcMeta>(&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<u32> = 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<FdGuard>,
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<DynamicProcInfo> = 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<FdGuard>,
}
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);
}
+213 -86
View File
@@ -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<A, E>(
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::<Header>()];
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<usize> {
pub fn fork_impl(args: &ForkArgs<'_>) -> Result<usize> {
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<usize> {
Ok(pid)
}
pub fn fork_inner(initial_rsp: *mut usize) -> Result<usize> {
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<usize> {
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<usize> {
// 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<usize> {
// 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<FdGuard>,
// 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::<usize>().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<NewChildProc> {
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<StaticProcInfo> =
SyncUnsafeCell::new(StaticProcInfo {
pid: 0,
proc_fd: MaybeUninit::zeroed(),
has_proc_fd: false,
});
+196
View File
@@ -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<Self> {
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<Self> {
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;
+89 -24
View File
@@ -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<impl FnOnce(u64) -> 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<Unreachable> {
},
&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<SiginfoAbi> {
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::<RtSigInfo>::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::<RtSigInfo>()];
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,
+243 -30
View File
@@ -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<T>(restart: bool, mut f: impl FnMut() -> Result<T>) -> Result<T> {
fn wrapper<T>(restart: bool, erestart: bool, mut f: impl FnMut() -> Result<T>) -> Result<T> {
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<T>(restart: bool, mut f: impl FnMut() -> Result<T>) -> Result<T> {
// TODO: uninitialized memory?
#[inline]
pub fn posix_read(fd: usize, buf: &mut [u8]) -> Result<usize> {
wrapper(true, || syscall::read(fd, buf))
wrapper(true, false, || syscall::read(fd, buf))
}
#[inline]
pub fn posix_write(fd: usize, buf: &[u8]) -> Result<usize> {
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<u32> {
)
.map(|awoken| awoken as u32)
}
pub fn sys_waitpid(pid: usize, status: &mut usize, flags: usize) -> Result<usize> {
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<usize> {
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<usize> {
proc_call(**crate::current_proc_fd(), payload, flags, metadata)
}
pub fn proc_call(
proc_fd: usize,
payload: &mut [u8],
flags: CallFlags,
metadata: &[u64],
) -> Result<usize> {
sys_call(proc_fd, payload, flags, metadata)
}
pub fn thread_call(
thread_fd: usize,
payload: &mut [u8],
flags: CallFlags,
metadata: &[u64],
) -> Result<usize> {
sys_call(thread_fd, payload, flags, metadata)
}
pub fn this_thread_call(payload: &mut [u8], flags: CallFlags, metadata: &[u64]) -> Result<usize> {
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<usize> {
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<T> {
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<Option<u32>>) -> 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::<u32>() * 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<u32> {
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<usize> {
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<usize> {
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(())
}
+10 -11
View File
@@ -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<FdGuard> {
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::<usize>() * 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!()
}
+4 -2
View File
@@ -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::<sigset_t>::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)
}
+14 -8
View File
@@ -569,15 +569,21 @@ pub extern "C" fn getppid() -> pid_t {
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getresgid.html>.
// #[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<T> write-only wrapper?
Sys::getresgid(rgid.as_mut(), egid.as_mut(), sgid.as_mut())
.map(|()| 0)
.or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getresuid.html>.
// #[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<T> write-only wrapper?
Sys::getresuid(ruid.as_mut(), euid.as_mut(), suid.as_mut())
.map(|()| 0)
.or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getsid.html>.
@@ -812,13 +818,13 @@ pub unsafe extern "C" fn set_default_scheme(scheme: *const c_char) -> c_int {
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setegid.html>.
// #[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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/seteuid.html>.
// #[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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setgid.html>.
+9 -3
View File
@@ -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")]
{
+25 -14
View File
@@ -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::<ProgramHeader64<Endianness>>();
const SIZEOF_PHDR32: usize = mem::size_of::<ProgramHeader32<Endianness>>();
@@ -120,7 +123,10 @@ pub fn static_init(sp: &'static Stack) {
tcb.masters_len = mem::size_of::<Master>();
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);
}
}
+21 -6
View File
@@ -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
+15 -3
View File
@@ -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)
}
}
+9
View File
@@ -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;
+31
View File
@@ -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(|_| ())
}
+42 -13
View File
@@ -280,21 +280,40 @@ impl<T: Write> Write for CountingWriter<T> {
// 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<Item = [usize; 2]> + '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<Self::Item> {
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::<Vec<_>>();
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<usize> {
auxv_iter(ptr).find_map(|[kind, value]| Some(value).filter(|_| kind == requested_kind))
}
pub fn get_auxv(auxvs: &[[usize; 2]], key: usize) -> Option<usize> {
auxvs
.binary_search_by_key(&key, |[entry_key, _]| *entry_key)
@@ -306,13 +325,23 @@ pub fn get_auxv(auxvs: &[[usize; 2]], key: usize) -> Option<usize> {
#[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),
+12
View File
@@ -131,6 +131,18 @@ pub trait Pal {
fn getrandom(buf: &mut [u8], flags: c_uint) -> Result<usize>;
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<()>;
+1 -1
View File
@@ -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};
+19 -13
View File
@@ -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,
+22 -11
View File
@@ -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()
}
+98 -56
View File
@@ -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<usize> {
.map(|val| (val - 1) / PAGE_SIZE * PAGE_SIZE)
}
fn cvt_uid(id: c_int) -> Result<Option<u32>> {
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<c_int> {
@@ -423,7 +433,7 @@ impl Pal for Sys {
}
fn getpgid(pid: pid_t) -> Result<pid_t> {
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<c_int> {
@@ -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<pid_t> {
let mut buf = [0; mem::size_of::<usize>()];
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<pid_t> {
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) -> ! {
+6 -3
View File
@@ -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<()> {
+11 -9
View File
@@ -210,19 +210,21 @@ unsafe extern "C" fn new_thread_shim(
mutex1: *const Mutex<MaybeUninit<OsTid>>,
mutex2: *const Mutex<u64>,
) -> ! {
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();
+13 -2
View File
@@ -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
+5 -2
View File
@@ -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)
+36
View File
@@ -0,0 +1,36 @@
#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#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);
}
+31
View File
@@ -0,0 +1,31 @@
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#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);
}
+22 -7
View File
@@ -1,21 +1,36 @@
#include <assert.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#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
}
+58
View File
@@ -0,0 +1,58 @@
#include <assert.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#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);
}