From 75b2dcaa455d15e812d68a2bf285271f5ba9f252 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 25 Jul 2024 19:28:10 +0200 Subject: [PATCH 01/27] WIP: implement sigqueue --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- redox-rt/src/sys.rs | 7 +++++++ src/header/signal/mod.rs | 11 +++++++++++ src/platform/pal/signal.rs | 4 +++- src/platform/redox/signal.rs | 11 +++++++++-- 6 files changed, 33 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f2ce4c5c5..34baa0b6ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -390,9 +390,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "62871f2d65009c0256aed1b9cfeeb8ac272833c404e13d53d400cd0dad7a2ac0" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index cc46f45476..668f1ac578 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ features = ["c_api"] sc = "0.2.3" [target.'cfg(target_os = "redox")'.dependencies] -redox_syscall = "0.5.4" +redox_syscall = "0.5.5" redox-rt = { path = "redox-rt" } redox-path = "0.2" redox_event = { git = "https://gitlab.redox-os.org/redox-os/event.git", default-features = false, features = ["redox_syscall"] } diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs index 2e2db37123..7566f63f38 100644 --- a/redox-rt/src/sys.rs +++ b/redox-rt/src/sys.rs @@ -44,6 +44,13 @@ pub fn posix_kill(pid: usize, sig: usize) -> Result<()> { } } #[inline] +pub fn posix_sigqueue(pid: usize, sig: usize, val: usize) -> Result<()> { + match wrapper(false, || unsafe { syscall::syscall3(101, pid, sig, val) }) { + Ok(_) | Err(Error { errno: EINTR }) => Ok(()), + Err(error) => Err(error), + } +} +#[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(()), diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs index ffde0d5166..f3c58111a5 100644 --- a/src/header/signal/mod.rs +++ b/src/header/signal/mod.rs @@ -67,6 +67,11 @@ pub struct siginfo { #[no_mangle] pub extern "C" fn _cbindgen_export_siginfo(a: siginfo) {} +pub union sigval { + pub si_int: c_int, + pub si_ptr: *mut c_void, +} + /// cbindgen:ignore pub type sigset_t = c_ulonglong; /// cbindgen:ignore @@ -78,6 +83,12 @@ pub type stack_t = sigaltstack; pub extern "C" fn kill(pid: pid_t, sig: c_int) -> c_int { Sys::kill(pid, sig) } +#[no_mangle] +pub extern "C" fn sigqueue(pid: pid_t, sig: c_int, val: sigval) -> c_int { + Sys::sigqueue(pid, sig, val) + .map(|()| 0) + .or_minus_one_errno() +} #[no_mangle] pub extern "C" fn killpg(pgrp: pid_t, sig: c_int) -> c_int { diff --git a/src/platform/pal/signal.rs b/src/platform/pal/signal.rs index e661eef4b7..b00525a834 100644 --- a/src/platform/pal/signal.rs +++ b/src/platform/pal/signal.rs @@ -2,7 +2,7 @@ use super::super::{types::*, Pal}; use crate::{ error::Errno, header::{ - signal::{sigaction, siginfo_t, sigset_t, stack_t}, + signal::{sigaction, siginfo_t, sigset_t, sigval, stack_t}, sys_time::itimerval, time::timespec, }, @@ -13,6 +13,8 @@ pub trait PalSignal: Pal { fn kill(pid: pid_t, sig: c_int) -> c_int; + fn sigqueue(pid: pid_t, sig: c_int, val: sigval) -> Result<(), Errno>; + fn killpg(pgrp: pid_t, sig: c_int) -> c_int; fn raise(sig: c_int) -> Result<(), Errno>; diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs index f6dfc9747b..1f71784f86 100644 --- a/src/platform/redox/signal.rs +++ b/src/platform/redox/signal.rs @@ -11,8 +11,8 @@ use crate::{ header::{ errno::{EINVAL, ENOSYS}, signal::{ - sigaction, siginfo_t, sigset_t, stack_t, SA_SIGINFO, SIG_BLOCK, SIG_DFL, SIG_IGN, - SIG_SETMASK, SIG_UNBLOCK, SS_DISABLE, SS_ONSTACK, + sigaction, siginfo_t, sigset_t, sigval, stack_t, SA_SIGINFO, SIG_BLOCK, SIG_DFL, + SIG_IGN, SIG_SETMASK, SIG_UNBLOCK, SS_DISABLE, SS_ONSTACK, }, sys_time::{itimerval, ITIMER_REAL}, time::timespec, @@ -55,6 +55,13 @@ impl PalSignal for Sys { fn kill(pid: pid_t, sig: c_int) -> c_int { e(redox_rt::sys::posix_kill(pid as usize, sig as usize).map(|()| 0)) as c_int } + fn sigqueue(pid: pid_t, sig: c_int, val: sigval) -> Result<(), Errno> { + Ok(redox_rt::sys::posix_sigqueue( + pid as usize, + sig as usize, + unsafe { val.si_ptr } as usize, + )?) + } fn killpg(pgrp: pid_t, sig: c_int) -> c_int { e(redox_rt::sys::posix_killpg(pgrp as usize, sig as usize).map(|()| 0)) as c_int From 47e07654cb4767ae25ae6a69c9ba09c7ddb16093 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 25 Jul 2024 19:44:43 +0200 Subject: [PATCH 02/27] WIP: use rt signal constants from sigabi --- redox-rt/src/signal.rs | 18 +++++++++++++----- redox-rt/src/sys.rs | 4 +++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 00bd3474c0..95443b01c4 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -1,14 +1,14 @@ use core::{ cell::{Cell, UnsafeCell}, ffi::c_int, - sync::atomic::{AtomicUsize, Ordering}, + sync::atomic::{AtomicU8, AtomicUsize, Ordering}, }; use syscall::{ - data::AtomicU64, Error, RawAction, Result, SetSighandlerData, SigProcControl, Sigcontrol, - SigcontrolFlags, EINVAL, ENOMEM, EPERM, SIGABRT, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, SIGILL, - SIGKILL, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTRAP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, - SIGWINCH, SIGXCPU, SIGXFSZ, + data::AtomicU64, Error, NonatomicUsize, RawAction, RealtimeSig, Result, SetSighandlerData, + SigProcControl, Sigcontrol, SigcontrolFlags, EINVAL, ENOMEM, EPERM, SIGABRT, SIGBUS, SIGCHLD, + SIGCONT, SIGFPE, SIGILL, SIGKILL, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTRAP, SIGTSTP, SIGTTIN, + SIGTTOU, SIGURG, SIGWINCH, SIGXCPU, SIGXFSZ, }; use crate::{arch::*, proc::FdGuard, sync::Mutex, RtTcb, Tcb}; @@ -443,6 +443,14 @@ pub(crate) static PROC_CONTROL_STRUCT: SigProcControl = SigProcControl { user_data: AtomicU64::new(0), } }; 64], + qhead: AtomicU8::new(0), + _rsvd: [0; 7], + queue: [const { + RealtimeSig { + arg: NonatomicUsize::new(0), + } + }; 32], + qtail: AtomicU8::new(0), }; fn combine_allowset([lo, hi]: [u64; 2]) -> u64 { diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs index 7566f63f38..97e7ce7385 100644 --- a/redox-rt/src/sys.rs +++ b/redox-rt/src/sys.rs @@ -45,7 +45,9 @@ pub fn posix_kill(pid: usize, sig: usize) -> Result<()> { } #[inline] pub fn posix_sigqueue(pid: usize, sig: usize, val: usize) -> Result<()> { - match wrapper(false, || unsafe { syscall::syscall3(101, pid, sig, val) }) { + match wrapper(false, || unsafe { + syscall::syscall3(syscall::SYS_SIGQUEUE, pid, sig, val) + }) { Ok(_) | Err(Error { errno: EINTR }) => Ok(()), Err(error) => Err(error), } From 13e7b776439e8984b5d5e857c286bc1ad3db46e9 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 28 Jul 2024 23:20:51 +0200 Subject: [PATCH 03/27] Implement basic support for (kernel) rt signals. --- include/bits/signal.h | 1 - redox-rt/src/arch/x86_64.rs | 32 +++++++++++---- redox-rt/src/signal.rs | 56 +++++++++++++++++--------- redox-rt/src/sys.rs | 2 +- src/header/signal/linux.rs | 2 + src/header/signal/mod.rs | 17 +++++--- src/platform/linux/signal.rs | 18 ++++++++- src/platform/redox/signal.rs | 2 +- tests/Makefile | 1 + tests/sigqueue.c | 78 ++++++++++++++++++++++++++++++++++++ 10 files changed, 170 insertions(+), 39 deletions(-) create mode 100644 tests/sigqueue.c diff --git a/include/bits/signal.h b/include/bits/signal.h index 57832b5e36..013540fd5f 100644 --- a/include/bits/signal.h +++ b/include/bits/signal.h @@ -5,7 +5,6 @@ #define SIG_IGN ((void (*)(int))1) #define SIG_ERR ((void (*)(int))-1) -struct siginfo; typedef struct siginfo siginfo_t; typedef unsigned long long sigset_t; diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs index d0d0f9bf04..a0756c1282 100644 --- a/redox-rt/src/arch/x86_64.rs +++ b/redox-rt/src/arch/x86_64.rs @@ -25,6 +25,9 @@ pub struct SigArea { pub tmp_rsp: usize, pub tmp_rax: usize, pub tmp_rdx: usize, + pub tmp_rdi: usize, + pub tmp_rsi: usize, + pub tmp_ptr: usize, pub altstack_top: usize, pub altstack_bottom: usize, @@ -170,6 +173,8 @@ asmfunction!(__relibc_internal_sigentry: [" mov fs:[{tcb_sa_off} + {sa_tmp_rsp}], rsp mov fs:[{tcb_sa_off} + {sa_tmp_rax}], rax 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 // First, select signal, always pick first available bit 1: @@ -201,11 +206,18 @@ asmfunction!(__relibc_internal_sigentry: [" jz 7f bt edx, eax // check if signal was sent to thread specifically - jc 2f // then continue as usual + jc 2f // if so, continue as usual - // otherwise, try clearing pending - lock btr [rip + {pctl} + {pctl_off_pending}], eax - jnc 1b + // otherwise, try (competitively) dequeueing realtime signal + mov esi, eax + mov eax, {SYS_SIGDEQUEUE} + mov rdi, fs:[0] + add rdi, {tcb_sa_off} + {sa_tmp_ptr} // out pointer of dequeued realtime sig + syscall + test eax, eax + jnz 1b // assumes error can only be EAGAIN + lea eax, [esi + 32] + jmp 9f 2: mov edx, eax shr edx, 5 @@ -220,7 +232,7 @@ asmfunction!(__relibc_internal_sigentry: [" // skip the sigaltstack logic. lea rdx, [rip + {pctl} + {pctl_off_actions}] - // LEA doesn't support x16, so just do two x8s. + // LEA doesn't support 16x, so just do two x8s. lea rdx, [rdx + 8 * rax] lea rdx, [rdx + 8 * rax] @@ -247,8 +259,8 @@ asmfunction!(__relibc_internal_sigentry: [" push fs:[{tcb_sc_off} + {sc_saved_rip}] push fs:[{tcb_sc_off} + {sc_saved_rflags}] - push rdi - push rsi + push fs:[{tcb_sa_off} + {sa_tmp_rdi}] + push fs:[{tcb_sa_off} + {sa_tmp_rsi}] push fs:[{tcb_sa_off} + {sa_tmp_rdx}] push rcx push fs:[{tcb_sa_off} + {sa_tmp_rax}] @@ -288,7 +300,7 @@ asmfunction!(__relibc_internal_sigentry: [" vextractf128 [rsp], ymm15, 1 5: push rax // selected signal - sub rsp, 8 + push fs:[{tcb_sa_off} + {sa_tmp_ptr}] mov rdi, rsp call {inner} @@ -383,6 +395,9 @@ __relibc_internal_sigentry_crit_third: sa_tmp_rsp = const offset_of!(SigArea, tmp_rsp), sa_tmp_rax = const offset_of!(SigArea, tmp_rax), 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_ptr = const offset_of!(SigArea, tmp_ptr), sa_altstack_top = const offset_of!(SigArea, altstack_top), sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom), sc_saved_rflags = const offset_of!(Sigcontrol, saved_archdep_reg), @@ -398,6 +413,7 @@ __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, ]); extern "C" { diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 95443b01c4..529d1b3564 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -1,14 +1,14 @@ use core::{ cell::{Cell, UnsafeCell}, - ffi::c_int, + ffi::{c_int, c_void}, sync::atomic::{AtomicU8, AtomicUsize, Ordering}, }; use syscall::{ - data::AtomicU64, Error, NonatomicUsize, RawAction, RealtimeSig, Result, SetSighandlerData, - SigProcControl, Sigcontrol, SigcontrolFlags, EINVAL, ENOMEM, EPERM, SIGABRT, SIGBUS, SIGCHLD, - SIGCONT, SIGFPE, SIGILL, SIGKILL, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTRAP, SIGTSTP, SIGTTIN, - SIGTTOU, SIGURG, SIGWINCH, SIGXCPU, SIGXFSZ, + data::AtomicU64, Error, NonatomicUsize, RawAction, Result, SetSighandlerData, SigProcControl, + Sigcontrol, SigcontrolFlags, EINVAL, ENOMEM, EPERM, SIGABRT, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, + SIGILL, SIGKILL, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTRAP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, + SIGWINCH, SIGXCPU, SIGXFSZ, }; use crate::{arch::*, proc::FdGuard, sync::Mutex, RtTcb, Tcb}; @@ -25,11 +25,12 @@ pub fn sighandler_function() -> usize { #[repr(C)] pub struct SigStack { #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] - _pad: [usize; 1], // pad to 16 bytes alignment + _pad: [usize; 0], // pad to 16 bytes alignment #[cfg(target_arch = "x86")] - _pad: [usize; 3], // pad to 16 bytes alignment + _pad: [usize; 2], // pad to 16 bytes alignment + sival: usize, sig_num: usize, // x86_64: 864 bytes @@ -119,10 +120,33 @@ unsafe fn inner(stack: &mut SigStack) { && let Some(sigaction) = handler.sigaction { //let _ = syscall::write(1, alloc::format!("SIGACTION {:p}\n", sigaction).as_bytes()); + // TODO: This struct is for practical reasons locked to Linux's ABI, but avoid redefining + // it here. Alternatively, check at compile time that the structs are equivalent. + #[repr(C)] + struct siginfo { + si_signo: c_int, + si_errno: c_int, + si_code: c_int, + si_pid: c_int, // TODO pid_t + si_uid: c_int, // TODO uid_t + si_addr: *mut c_void, + si_status: c_int, + si_value: usize, // TODO union + } + let info = siginfo { + si_signo: stack.sig_num as c_int, + si_addr: core::ptr::null_mut(), + si_code: 0, // TODO: SIG_QUEUE/SIG_USER/etc + si_errno: 0, + si_pid: 0, + si_status: 0, + si_uid: 0, + si_value: stack.sival, + }; sigaction( stack.sig_num as c_int, - core::ptr::null_mut(), - core::ptr::null_mut(), + core::ptr::addr_of!(info).cast(), + core::ptr::null_mut(), // TODO ); } else if let Some(handler) = handler.handler { //let _ = syscall::write(1, alloc::format!("HANDLER {:p}\n", handler).as_bytes()); @@ -232,9 +256,9 @@ fn modify_sigmask(old: Option<&mut u64>, op: Option u32 // POSIX requires that at least one pending unblocked signal be delivered before // pthread_sigmask returns, if there is one. Deliver the lowest-numbered one. if can_raise != 0 { - let signal = can_raise.trailing_zeros() + 1; - // TODO - crate::sys::posix_kill_thread(**RtTcb::current().thread_fd(), signal); + unsafe { + manually_enter_trampoline(); + } } Ok(()) @@ -443,14 +467,6 @@ pub(crate) static PROC_CONTROL_STRUCT: SigProcControl = SigProcControl { user_data: AtomicU64::new(0), } }; 64], - qhead: AtomicU8::new(0), - _rsvd: [0; 7], - queue: [const { - RealtimeSig { - arg: NonatomicUsize::new(0), - } - }; 32], - qtail: AtomicU8::new(0), }; fn combine_allowset([lo, hi]: [u64; 2]) -> u64 { diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs index 97e7ce7385..e3a1db8892 100644 --- a/redox-rt/src/sys.rs +++ b/redox-rt/src/sys.rs @@ -46,7 +46,7 @@ pub fn posix_kill(pid: usize, sig: usize) -> Result<()> { #[inline] pub fn posix_sigqueue(pid: usize, sig: usize, val: usize) -> Result<()> { match wrapper(false, || unsafe { - syscall::syscall3(syscall::SYS_SIGQUEUE, pid, sig, val) + syscall::syscall3(syscall::SYS_SIGENQUEUE, pid, sig, val) }) { Ok(_) | Err(Error { errno: EINTR }) => Ok(()), Err(error) => Err(error), diff --git a/src/header/signal/linux.rs b/src/header/signal/linux.rs index fb23a0e99b..f17a55c6c3 100644 --- a/src/header/signal/linux.rs +++ b/src/header/signal/linux.rs @@ -76,3 +76,5 @@ pub const SS_DISABLE: usize = 2; // Those two should be updated from kernel headers pub const MINSIGSTKSZ: usize = 2048; pub const SIGSTKSZ: usize = 8096; + +pub const SI_QUEUE: i32 = -1; diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs index f3c58111a5..6fe93ed266 100644 --- a/src/header/signal/mod.rs +++ b/src/header/signal/mod.rs @@ -55,21 +55,26 @@ pub struct sigaltstack { } #[repr(C)] -#[derive(Clone, Debug)] +#[derive(Clone, Copy)] pub struct siginfo { pub si_signo: c_int, pub si_errno: c_int, pub si_code: c_int, - _padding: [c_int; 29], - _si_align: [usize; 0], + pub si_pid: pid_t, + pub si_uid: uid_t, + pub si_addr: *mut c_void, + pub si_status: c_int, + pub si_value: sigval, } #[no_mangle] pub extern "C" fn _cbindgen_export_siginfo(a: siginfo) {} +#[derive(Clone, Copy)] +#[repr(C)] pub union sigval { - pub si_int: c_int, - pub si_ptr: *mut c_void, + pub sival_int: c_int, + pub sival_ptr: *mut c_void, } /// cbindgen:ignore @@ -371,7 +376,7 @@ pub unsafe extern "C" fn sigwait(set: *const sigset_t, sig: *mut c_int) -> c_int #[no_mangle] pub unsafe extern "C" fn sigtimedwait( set: *const sigset_t, - sig: *mut siginfo_t, + sig: *mut siginfo, // https://github.com/mozilla/cbindgen/issues/621 tp: *const timespec, ) -> c_int { Sys::sigtimedwait(set, sig, tp) diff --git a/src/platform/linux/signal.rs b/src/platform/linux/signal.rs index d25a6139ef..b1d4d2e068 100644 --- a/src/platform/linux/signal.rs +++ b/src/platform/linux/signal.rs @@ -1,4 +1,5 @@ -use core::mem; +use crate::header::signal::sigval; +use core::{mem, ptr::addr_of}; use super::{ super::{types::*, PalSignal}, @@ -7,7 +8,7 @@ use super::{ use crate::{ error::Errno, header::{ - signal::{sigaction, siginfo_t, sigset_t, stack_t, NSIG, SA_RESTORER}, + signal::{sigaction, siginfo_t, sigset_t, stack_t, NSIG, SA_RESTORER, SI_QUEUE}, sys_time::itimerval, time::timespec, }, @@ -21,6 +22,19 @@ impl PalSignal for Sys { fn kill(pid: pid_t, sig: c_int) -> c_int { e(unsafe { syscall!(KILL, pid, sig) }) as c_int } + fn sigqueue(pid: pid_t, sig: c_int, val: sigval) -> Result<(), Errno> { + let info = siginfo_t { + si_addr: core::ptr::null_mut(), + si_code: SI_QUEUE, + si_errno: 0, + si_pid: 0, // TODO: GETPID? + si_signo: sig, + si_status: 0, + si_uid: 0, // TODO: GETUID? + si_value: val, + }; + e_raw(unsafe { syscall!(RT_SIGQUEUEINFO, pid, sig, addr_of!(info)) }).map(|_| ()) + } fn killpg(pgrp: pid_t, sig: c_int) -> c_int { e(unsafe { syscall!(KILL, -(pgrp as isize) as pid_t, sig) }) as c_int diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs index 1f71784f86..6f343754c5 100644 --- a/src/platform/redox/signal.rs +++ b/src/platform/redox/signal.rs @@ -59,7 +59,7 @@ impl PalSignal for Sys { Ok(redox_rt::sys::posix_sigqueue( pid as usize, sig as usize, - unsafe { val.si_ptr } as usize, + unsafe { val.sival_ptr } as usize, )?) } diff --git a/tests/Makefile b/tests/Makefile index 774a03e86a..b2ce84cbf7 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -163,6 +163,7 @@ NAMES=\ sa_restart \ sigchld \ stdio/ctermid \ + sigqueue \ stdio/tempnam \ stdio/tmpnam \ stdlib/bsearch \ diff --git a/tests/sigqueue.c b/tests/sigqueue.c new file mode 100644 index 0000000000..a69df9645b --- /dev/null +++ b/tests/sigqueue.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +#define THE_SIG SIGRTMIN + +volatile sig_atomic_t num = 1; + +void action(int sig, siginfo_t *info, void *context) { + (void)context; + assert(sig == THE_SIG); + assert(info->si_signo == THE_SIG); + assert(info->si_value.sival_int == num); + num++; + write(1, "action\n", 7); +} + +int main(void) { + int status, fds[2]; + + status = pipe(fds); + ERROR_IF(pipe, status, == -1); + + int child = fork(); + ERROR_IF(fork, child, == -1); + + status = close(fds[child == 0 ? 0 : 1]); + ERROR_IF(close, status, == -1); + + sigset_t set; + status = sigfillset(&set); + ERROR_IF(sigfillset, status, == -1); + status = sigdelset(&set, SIGSEGV); + ERROR_IF(sigdelset, status, == -1); + status = sigdelset(&set, SIGBUS); + ERROR_IF(sigdelset, status, == -1); + status = sigdelset(&set, SIGILL); + ERROR_IF(sigdelset, status, == -1); + status = sigdelset(&set, SIGFPE); + ERROR_IF(sigdelset, status, == -1); + status = sigdelset(&set, SIGINT); + ERROR_IF(sigdelset, status, == -1); + status = sigprocmask(SIG_SETMASK, &set, NULL); + ERROR_IF(sigprocmask, status, == -1); + + struct sigaction sa; + memcpy(&sa.sa_mask, &set, sizeof (sigset_t)); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = action; + + status = sigaction(THE_SIG, &sa, NULL); + ERROR_IF(sigaction, status, == -1); + + if (child == 0) { + status = sigemptyset(&set); + ERROR_IF(sigemptyset, status, == -1); + while (num != 32) { + } + status = write(fds[1], "A", 1); + ERROR_IF(write, status, == -1); + } else { + for (int n = 1; n <= 32; n++) { + status = sigqueue(child, THE_SIG, (union sigval){ .sival_int = n }); + ERROR_IF(sigqueue, status, == -1); + } + char buf[1]; + status = read(fds[0], buf, 1); + ERROR_IF(read, status, == -1); + } + + return 0; +} From b48da5622d5e21583521838f20abf27fb1f9c858 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 30 Jul 2024 10:41:55 +0200 Subject: [PATCH 04/27] Add rtsig to i686 trampoline too. --- redox-rt/src/arch/i686.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/redox-rt/src/arch/i686.rs b/redox-rt/src/arch/i686.rs index 1f0cbdd9da..9889ff3a2b 100644 --- a/redox-rt/src/arch/i686.rs +++ b/redox-rt/src/arch/i686.rs @@ -22,6 +22,7 @@ pub struct SigArea { pub tmp_eax: usize, pub tmp_ecx: usize, pub tmp_edx: usize, + pub tmp_ptr: usize, pub pctl: usize, // TODO: reference pctl directly pub disable_signals_depth: u64, pub last_sig_was_restart: bool, @@ -166,12 +167,21 @@ asmfunction!(__relibc_internal_sigentry: [" jz 7f // spurious signal bsf eax, eax + // If thread was specifically targeted, send the signal to it first. bt edx, eax jc 8f - lock btr [ecx + {pctl_word} + 4], eax - jnc 1b - add eax, 32 + mov edx, ebx + lea ecx, [eax+32] + mov eax, {SYS_SIGDEQUEUE} + mov edx, gs:[0] + add edx, {tcb_sa_off} + {sa_tmp_ptr} + int 0x80 + mov ebx, edx + test eax, eax + jnz 1b + + mov eax, ecx jmp 2f 8: add eax, 32 @@ -200,7 +210,7 @@ asmfunction!(__relibc_internal_sigentry: [" push dword ptr gs:[{tcb_sc_off} + {sc_saved_eflags}] push dword ptr gs:[{tcb_sa_off} + {sa_tmp_edx}] - push ecx + push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ecx}] push dword ptr gs:[{tcb_sa_off} + {sa_tmp_eax}] push ebx push edi @@ -211,7 +221,8 @@ asmfunction!(__relibc_internal_sigentry: [" fxsave [esp] push eax - sub esp, 3 * 4 + push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ptr}] + sub esp, 2 * 4 mov ecx, esp call {inner} @@ -262,6 +273,7 @@ __relibc_internal_sigentry_crit_third: sa_tmp_eax = const offset_of!(SigArea, tmp_eax), sa_tmp_ecx = const offset_of!(SigArea, tmp_ecx), sa_tmp_edx = const offset_of!(SigArea, tmp_edx), + sa_tmp_ptr = const offset_of!(SigArea, tmp_ptr), 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), @@ -275,6 +287,7 @@ __relibc_internal_sigentry_crit_third: pctl_word = const offset_of!(SigProcControl, pending), pctl = sym PROC_CONTROL_STRUCT, STACK_ALIGN = const 16, + SYS_SIGDEQUEUE = const syscall::SYS_SIGDEQUEUE, ]); asmfunction!(__relibc_internal_rlct_clone_ret -> usize: [" From abc2ec7bb5382fcf0aa865272680f45062b7fdef Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 30 Jul 2024 23:24:42 +0200 Subject: [PATCH 05/27] Add prototype ucontext_t support. --- redox-rt/src/arch/i686.rs | 6 ++-- redox-rt/src/arch/x86_64.rs | 5 +++- redox-rt/src/signal.rs | 55 +++++++++++++++++++++++++----------- src/platform/linux/signal.rs | 42 +++++++++++++++++++++++++++ src/platform/redox/signal.rs | 53 ++++++++++++++++++++++++++++++++-- 5 files changed, 138 insertions(+), 23 deletions(-) diff --git a/redox-rt/src/arch/i686.rs b/redox-rt/src/arch/i686.rs index 9889ff3a2b..f425a6c1f0 100644 --- a/redox-rt/src/arch/i686.rs +++ b/redox-rt/src/arch/i686.rs @@ -222,13 +222,13 @@ asmfunction!(__relibc_internal_sigentry: [" push eax push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ptr}] - sub esp, 2 * 4 + sub esp, 24 mov ecx, esp call {inner} - fxrstor [esp + 16] - add esp, 16 + 29 * 16 + 2 * 4 + fxrstor [esp + 32] + add esp, 32 + 29 * 16 + 2 * 4 pop ebp pop esi diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs index a0756c1282..1faf2ab0cb 100644 --- a/redox-rt/src/arch/x86_64.rs +++ b/redox-rt/src/arch/x86_64.rs @@ -1,5 +1,6 @@ use core::{ mem::offset_of, + ptr::NonNull, sync::atomic::{AtomicU8, Ordering}, }; @@ -33,6 +34,7 @@ pub struct SigArea { pub altstack_bottom: usize, pub disable_signals_depth: u64, pub last_sig_was_restart: bool, + pub last_sigstack: Option>, } #[repr(C, align(16))] @@ -301,11 +303,12 @@ asmfunction!(__relibc_internal_sigentry: [" 5: push rax // selected signal push fs:[{tcb_sa_off} + {sa_tmp_ptr}] + sub rsp, 48 // alloc space for ucontext fields mov rdi, rsp call {inner} - add rsp, 16 + add rsp, 64 fxrstor64 [rsp + 16 * 16] diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 529d1b3564..4e9ca55cd7 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -1,6 +1,7 @@ use core::{ cell::{Cell, UnsafeCell}, ffi::{c_int, c_void}, + ptr::NonNull, sync::atomic::{AtomicU8, AtomicUsize, Ordering}, }; @@ -22,14 +23,19 @@ pub fn sighandler_function() -> usize { __relibc_internal_sigentry as usize } +/// ucontext_t representation #[repr(C)] pub struct SigStack { #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] - _pad: [usize; 0], // pad to 16 bytes alignment + _pad: [usize; 1], // pad from 7*8 to 64 #[cfg(target_arch = "x86")] - _pad: [usize; 2], // pad to 16 bytes alignment + _pad: [usize; 0], // don't pad from 8*4 + pub link: *mut SigStack, + + pub old_stack: PosixStackt, + pub old_mask: u64, sival: usize, sig_num: usize, @@ -38,10 +44,35 @@ pub struct SigStack { // aarch64: 272 bytes (SIMD TODO) pub regs: ArchIntRegs, } +#[repr(C)] +pub struct PosixStackt { + pub sp: *mut (), + pub flags: i32, + pub size: usize, +} + +#[repr(C)] +// TODO: This struct is for practical reasons locked to Linux's ABI, but avoid redefining +// it here. Alternatively, check at compile time that the structs are equivalent. +pub struct SiginfoAbi { + pub si_signo: i32, + pub si_errno: i32, + pub si_code: i32, + pub si_pid: i32, // pid_t + pub si_uid: i32, // uid_t + pub si_addr: *mut (), // *mut c_void + pub si_status: i32, + pub si_value: usize, // sigval +} #[inline(always)] unsafe fn inner(stack: &mut SigStack) { let os = &Tcb::current().unwrap().os_specific; + + let stack_ptr = NonNull::from(&mut *stack); + stack.link = core::mem::replace(&mut (*os.arch.get()).last_sigstack, Some(stack_ptr)) + .map_or_else(core::ptr::null_mut, |x| x.as_ptr()); + let signals_were_disabled = (*os.arch.get()).disable_signals_depth > 0; let _targeted_thread_not_process = stack.sig_num >= 64; @@ -119,21 +150,11 @@ unsafe fn inner(stack: &mut SigStack) { if sigaction.flags.contains(SigactionFlags::SIGINFO) && let Some(sigaction) = handler.sigaction { + stack.old_mask = ((prev_w1 >> 32) << 32) | (prev_w0 & 0xffff_ffff); + // TODO: stack.old_stack + //let _ = syscall::write(1, alloc::format!("SIGACTION {:p}\n", sigaction).as_bytes()); - // TODO: This struct is for practical reasons locked to Linux's ABI, but avoid redefining - // it here. Alternatively, check at compile time that the structs are equivalent. - #[repr(C)] - struct siginfo { - si_signo: c_int, - si_errno: c_int, - si_code: c_int, - si_pid: c_int, // TODO pid_t - si_uid: c_int, // TODO uid_t - si_addr: *mut c_void, - si_status: c_int, - si_value: usize, // TODO union - } - let info = siginfo { + let info = SiginfoAbi { si_signo: stack.sig_num as c_int, si_addr: core::ptr::null_mut(), si_code: 0, // TODO: SIG_QUEUE/SIG_USER/etc @@ -146,7 +167,7 @@ unsafe fn inner(stack: &mut SigStack) { sigaction( stack.sig_num as c_int, core::ptr::addr_of!(info).cast(), - core::ptr::null_mut(), // TODO + stack as *mut SigStack as *mut (), ); } else if let Some(handler) = handler.handler { //let _ = syscall::write(1, alloc::format!("HANDLER {:p}\n", handler).as_bytes()); diff --git a/src/platform/linux/signal.rs b/src/platform/linux/signal.rs index b1d4d2e068..a6ec22fb36 100644 --- a/src/platform/linux/signal.rs +++ b/src/platform/linux/signal.rs @@ -14,6 +14,48 @@ use crate::{ }, }; +// Mirrors the ucontext_t struct from the libc crate on Linux. +#[repr(C)] +pub struct ucontext_t { + pub uc_flags: c_ulong, + pub uc_link: *mut ucontext_t, + pub uc_stack: stack_t, + pub uc_mcontext: mcontext_t, + pub uc_sigmask: sigset_t, + __private: [u8; 512], +} +#[repr(C)] +pub struct _libc_fpstate { + pub cwd: u16, + pub swd: u16, + pub ftw: u16, + pub fop: u16, + pub rip: u64, + pub rdp: u64, + pub mxcsr: u32, + pub mxcr_mask: u32, + pub _st: [_libc_fpxreg; 8], + pub _xmm: [_libc_xmmreg; 16], + __private: [u64; 12], +} +#[repr(C)] +pub struct _libc_fpxreg { + pub significand: [u16; 4], + pub exponent: u16, + __private: [u16; 3], +} + +#[repr(C)] +pub struct _libc_xmmreg { + pub element: [u32; 4], +} +#[repr(C)] +pub struct mcontext_t { + pub gregs: [i64; 23], // TODO: greg_t? + pub fpregs: *mut _libc_fpstate, + __private: [u64; 8], +} + impl PalSignal for Sys { unsafe fn getitimer(which: c_int, out: *mut itimerval) -> c_int { e(syscall!(GETITIMER, which, out)) as c_int diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs index 6f343754c5..3be1da8441 100644 --- a/src/platform/redox/signal.rs +++ b/src/platform/redox/signal.rs @@ -1,5 +1,7 @@ -use core::mem; -use redox_rt::signal::{Sigaction, SigactionFlags, SigactionKind, Sigaltstack, SignalHandler}; +use core::mem::{self, offset_of}; +use redox_rt::signal::{ + SigStack, Sigaction, SigactionFlags, SigactionKind, Sigaltstack, SignalHandler, +}; use syscall::{self, Result}; use super::{ @@ -20,6 +22,53 @@ use crate::{ platform::ERRNO, }; +#[repr(C)] +pub struct ucontext_t { + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] + _pad: [usize; 1], // pad from 7*8 to 64 + + #[cfg(target_arch = "x86")] + _pad: [usize; 0], // don't pad from 8*4 + + pub uc_link: *mut ucontext_t, + pub uc_stack: stack_t, + pub uc_sigmask: sigset_t, + _sival: usize, + _signum: usize, + pub uc_mcontext: mcontext_t, +} + +const _: () = { + const fn assert_eq(a: usize, b: usize) { + if a != b { + panic!("compile-time struct verification failed"); + } + } + assert_eq(offset_of!(ucontext_t, uc_link), offset_of!(SigStack, link)); + assert_eq( + offset_of!(ucontext_t, uc_stack), + offset_of!(SigStack, old_stack), + ); + assert_eq( + offset_of!(ucontext_t, uc_sigmask), + offset_of!(SigStack, old_mask), + ); + assert_eq( + offset_of!(ucontext_t, uc_mcontext), + offset_of!(SigStack, regs), + ); +}; + +#[repr(C)] +pub struct mcontext_t { + #[cfg(target_arch = "x86")] + _opaque: [u8; 512], + #[cfg(target_arch = "x86-64")] + _opaque: [u8; 864], + #[cfg(target_arch = "aarch64")] + _opaque: [u8; 272], +} + impl PalSignal for Sys { unsafe fn getitimer(which: c_int, out: *mut itimerval) -> c_int { let path = match which { From cdae79c1297141e0bd29a6dbddeba0c19f80ae08 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 31 Jul 2024 14:58:40 +0200 Subject: [PATCH 06/27] Fix i686 compilation. --- redox-rt/src/arch/i686.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/redox-rt/src/arch/i686.rs b/redox-rt/src/arch/i686.rs index f425a6c1f0..8db0140af6 100644 --- a/redox-rt/src/arch/i686.rs +++ b/redox-rt/src/arch/i686.rs @@ -1,4 +1,4 @@ -use core::{mem::offset_of, sync::atomic::Ordering}; +use core::{mem::offset_of, ptr::NonNull, sync::atomic::Ordering}; use syscall::*; @@ -26,6 +26,7 @@ pub struct SigArea { pub pctl: usize, // TODO: reference pctl directly pub disable_signals_depth: u64, pub last_sig_was_restart: bool, + pub last_sigstack: Option>, } #[derive(Debug, Default)] #[repr(C, align(16))] From cce268afbf8e437d2bd599ae92fa0971741f8543 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 31 Jul 2024 15:51:16 +0200 Subject: [PATCH 07/27] Pass old stack pointer in uc_stack. --- redox-rt/src/arch/i686.rs | 15 ++++++++++----- redox-rt/src/arch/x86_64.rs | 17 ++++++++++++----- redox-rt/src/signal.rs | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/redox-rt/src/arch/i686.rs b/redox-rt/src/arch/i686.rs index 8db0140af6..67213b9828 100644 --- a/redox-rt/src/arch/i686.rs +++ b/redox-rt/src/arch/i686.rs @@ -4,7 +4,7 @@ use syscall::*; use crate::{ proc::{fork_inner, FdGuard}, - signal::{inner_fastcall, RtSigarea, SigStack, PROC_CONTROL_STRUCT}, + signal::{inner_fastcall, PosixStackt, RtSigarea, SigStack, PROC_CONTROL_STRUCT}, RtTcb, }; @@ -314,16 +314,21 @@ extern "C" { fn __relibc_internal_sigentry_crit_second(); fn __relibc_internal_sigentry_crit_third(); } -pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) { +pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt { if stack.regs.eip == __relibc_internal_sigentry_crit_first as usize { let stack_ptr = stack.regs.esp as *const usize; stack.regs.esp = stack_ptr.read(); stack.regs.eip = stack_ptr.sub(1).read(); - } else if stack.regs.eip == __relibc_internal_sigentry_crit_second as usize { - stack.regs.eip = area.tmp_eip; - } else if stack.regs.eip == __relibc_internal_sigentry_crit_third as usize { + } else if stack.regs.eip == __relibc_internal_sigentry_crit_second as usize + || stack.regs.eip == __relibc_internal_sigentry_crit_third as usize + { stack.regs.eip = area.tmp_eip; } + PosixStackt { + sp: stack.regs.esp as *mut (), + size: 0, // TODO + flags: 0, // TODO + } } #[no_mangle] pub unsafe fn manually_enter_trampoline() { diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs index 1faf2ab0cb..74c6f5a491 100644 --- a/redox-rt/src/arch/x86_64.rs +++ b/redox-rt/src/arch/x86_64.rs @@ -11,7 +11,7 @@ use syscall::{ use crate::{ proc::{fork_inner, FdGuard}, - signal::{inner_c, RtSigarea, SigStack, PROC_CONTROL_STRUCT}, + signal::{inner_c, PosixStackt, RtSigarea, SigStack, PROC_CONTROL_STRUCT}, RtTcb, Tcb, }; @@ -424,7 +424,8 @@ extern "C" { fn __relibc_internal_sigentry_crit_second(); fn __relibc_internal_sigentry_crit_third(); } -pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) { +/// Fixes some edge cases, and calculates the value for uc_stack. +pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt { // It is impossible to update RSP and RIP atomically on x86_64, without using IRETQ, which is // almost as slow as calling a SIGRETURN syscall would be. Instead, we abuse the fact that // signals are disabled in the prologue of the signal trampoline, which allows us to emulate @@ -438,12 +439,18 @@ pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) { let stack_ptr = stack.regs.rsp as *const usize; stack.regs.rsp = stack_ptr.read(); stack.regs.rip = stack_ptr.sub(1).read(); - } else if stack.regs.rip == __relibc_internal_sigentry_crit_second as usize { + } else if stack.regs.rip == __relibc_internal_sigentry_crit_second as usize + || stack.regs.rip == __relibc_internal_sigentry_crit_third as usize + { // Almost finished, just reexecute the jump before tmp_rip is overwritten by this // deeper-level signal. stack.regs.rip = area.tmp_rip; - } else if stack.regs.rip == __relibc_internal_sigentry_crit_third as usize { - stack.regs.rip = area.tmp_rip; + } + + PosixStackt { + sp: stack.regs.rsp as *mut (), + size: 0, // TODO + flags: 0, // TODO } } diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 4e9ca55cd7..49c3b47f8b 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -80,7 +80,7 @@ unsafe fn inner(stack: &mut SigStack) { // asm counts from 0 stack.sig_num += 1; - arch_pre(stack, &mut *os.arch.get()); + stack.old_stack = arch_pre(stack, &mut *os.arch.get()); let sigaction = { let guard = SIGACTIONS_LOCK.lock(); From 6fe7c264f630de17f4d53370ab3022bbea72336e Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 1 Aug 2024 18:29:53 +0200 Subject: [PATCH 08/27] Pass si_code to siginfo_t struct. --- redox-rt/src/arch/x86_64.rs | 28 ++++++++++++++++++++-------- redox-rt/src/signal.rs | 30 +++++++++++++++--------------- redox-rt/src/sys.rs | 12 +++++++++--- src/header/signal/linux.rs | 1 + src/header/signal/redox.rs | 3 +++ tests/sigqueue.c | 1 + 6 files changed, 49 insertions(+), 26 deletions(-) diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs index 74c6f5a491..d918a18189 100644 --- a/redox-rt/src/arch/x86_64.rs +++ b/redox-rt/src/arch/x86_64.rs @@ -7,6 +7,7 @@ use core::{ use syscall::{ data::{SigProcControl, Sigcontrol}, error::*, + RtSigInfo, }; use crate::{ @@ -28,7 +29,7 @@ pub struct SigArea { pub tmp_rdx: usize, pub tmp_rdi: usize, pub tmp_rsi: usize, - pub tmp_ptr: usize, + pub tmp_inf: RtSigInfo, pub altstack_top: usize, pub altstack_bottom: usize, @@ -214,7 +215,7 @@ asmfunction!(__relibc_internal_sigentry: [" mov esi, eax mov eax, {SYS_SIGDEQUEUE} mov rdi, fs:[0] - add rdi, {tcb_sa_off} + {sa_tmp_ptr} // out pointer of dequeued realtime sig + add rdi, {tcb_sa_off} + {sa_tmp_inf} // out pointer of dequeued realtime sig syscall test eax, eax jnz 1b // assumes error can only be EAGAIN @@ -301,9 +302,8 @@ asmfunction!(__relibc_internal_sigentry: [" vextractf128 [rsp + 16], ymm14, 1 vextractf128 [rsp], ymm15, 1 5: - push rax // selected signal - push fs:[{tcb_sa_off} + {sa_tmp_ptr}] - sub rsp, 48 // alloc space for ucontext fields + mov [rsp - 8], eax + sub rsp, 64 // alloc space for ucontext fields mov rdi, rsp call {inner} @@ -400,7 +400,7 @@ __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_ptr = const offset_of!(SigArea, tmp_ptr), + sa_tmp_inf = const offset_of!(SigArea, tmp_inf), sa_altstack_top = const offset_of!(SigArea, altstack_top), sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom), sc_saved_rflags = const offset_of!(Sigcontrol, saved_archdep_reg), @@ -425,7 +425,11 @@ extern "C" { fn __relibc_internal_sigentry_crit_third(); } /// Fixes some edge cases, and calculates the value for uc_stack. -pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt { +pub unsafe fn arch_pre( + stack: &mut SigStack, + area: &mut SigArea, + targeted_thread: bool, +) -> PosixStackt { // It is impossible to update RSP and RIP atomically on x86_64, without using IRETQ, which is // almost as slow as calling a SIGRETURN syscall would be. Instead, we abuse the fact that // signals are disabled in the prologue of the signal trampoline, which allows us to emulate @@ -447,6 +451,14 @@ pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt stack.regs.rip = area.tmp_rip; } + stack.sig_code = if (stack.sig_num - 1) / 32 == 1 && !targeted_thread { + area.tmp_inf.code as u32 + } else { + // TODO: SIGCHLD information when applicable + 0 + }; + stack.sival = area.tmp_inf.arg; + PosixStackt { sp: stack.regs.rsp as *mut (), size: 0, // TODO @@ -456,7 +468,7 @@ pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt pub(crate) static SUPPORTS_AVX: AtomicU8 = AtomicU8::new(0); -// __relibc will be prepended to the name, so mangling is fine +// __relibc will be prepended to the name, so no_mangle is fine #[no_mangle] pub unsafe fn manually_enter_trampoline() { let c = &Tcb::current().unwrap().os_specific.control; diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 49c3b47f8b..300564c1fd 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -30,14 +30,15 @@ pub struct SigStack { _pad: [usize; 1], // pad from 7*8 to 64 #[cfg(target_arch = "x86")] - _pad: [usize; 0], // don't pad from 8*4 + _pad: [usize; 3], // pad from 9*4 to 12*4 pub link: *mut SigStack, pub old_stack: PosixStackt, pub old_mask: u64, - sival: usize, - sig_num: usize, + pub(crate) sival: usize, + pub(crate) sig_num: u32, + pub(crate) sig_code: u32, // x86_64: 864 bytes // i686: 512 bytes @@ -75,16 +76,16 @@ unsafe fn inner(stack: &mut SigStack) { let signals_were_disabled = (*os.arch.get()).disable_signals_depth > 0; - let _targeted_thread_not_process = stack.sig_num >= 64; + let targeted_thread_not_process = stack.sig_num >= 64; stack.sig_num %= 64; // asm counts from 0 stack.sig_num += 1; - stack.old_stack = arch_pre(stack, &mut *os.arch.get()); + stack.old_stack = arch_pre(stack, &mut *os.arch.get(), targeted_thread_not_process); let sigaction = { let guard = SIGACTIONS_LOCK.lock(); - let action = convert_old(&PROC_CONTROL_STRUCT.actions[stack.sig_num - 1]); + let action = convert_old(&PROC_CONTROL_STRUCT.actions[stack.sig_num as usize - 1]); if action.flags.contains(SigactionFlags::RESETHAND) { // TODO: other things that must be set drop(guard); @@ -107,7 +108,7 @@ unsafe fn inner(stack: &mut SigStack) { panic!("ctl {:x?} signal {}", os.control, stack.sig_num) } SigactionKind::Default => { - syscall::exit(stack.sig_num); + syscall::exit(stack.sig_num as usize); unreachable!(); } SigactionKind::Handled { handler } => handler, @@ -150,18 +151,17 @@ unsafe fn inner(stack: &mut SigStack) { if sigaction.flags.contains(SigactionFlags::SIGINFO) && let Some(sigaction) = handler.sigaction { - stack.old_mask = ((prev_w1 >> 32) << 32) | (prev_w0 & 0xffff_ffff); - // TODO: stack.old_stack + stack.old_mask = ((prev_w1 >> 32) << 32) | (prev_w0 >> 32); //let _ = syscall::write(1, alloc::format!("SIGACTION {:p}\n", sigaction).as_bytes()); let info = SiginfoAbi { si_signo: stack.sig_num as c_int, si_addr: core::ptr::null_mut(), - si_code: 0, // TODO: SIG_QUEUE/SIG_USER/etc + si_code: stack.sig_code as i32, si_errno: 0, - si_pid: 0, + si_pid: 0, // TODO si_status: 0, - si_uid: 0, + si_uid: 0, // TODO si_value: stack.sival, }; sigaction( @@ -185,11 +185,11 @@ unsafe fn inner(stack: &mut SigStack) { // Update allowset again. //let _ = syscall::write(1, &alloc::format!("WORD2 {:x?}\n", os.control.word).as_bytes()); - let prev_w0 = os.control.word[0].fetch_add( + let _prev_w0 = os.control.word[0].fetch_add( (prev_sigallow_lo << 32).wrapping_sub(sigallow_inside_lo << 32), Ordering::Relaxed, ); - let prev_w1 = os.control.word[1].fetch_add( + let _prev_w1 = os.control.word[1].fetch_add( (prev_sigallow_hi << 32).wrapping_sub(sigallow_inside_hi << 32), Ordering::Relaxed, ); @@ -494,7 +494,7 @@ fn combine_allowset([lo, hi]: [u64; 2]) -> u64 { (lo >> 32) | ((hi >> 32) << 32) } -const fn sig_bit(sig: usize) -> u64 { +const fn sig_bit(sig: u32) -> u64 { //assert_ne!(sig, 32); //assert_ne!(sig, 0); 1 << (sig - 1) diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs index e3a1db8892..78fa0879e9 100644 --- a/redox-rt/src/sys.rs +++ b/redox-rt/src/sys.rs @@ -1,6 +1,8 @@ +use core::ptr::addr_of; + use syscall::{ error::{Error, Result, EINTR}, - TimeSpec, + RtSigInfo, TimeSpec, }; use crate::{arch::manually_enter_trampoline, proc::FdGuard, signal::tmp_disable_signals, Tcb}; @@ -44,9 +46,13 @@ pub fn posix_kill(pid: usize, sig: usize) -> Result<()> { } } #[inline] -pub fn posix_sigqueue(pid: usize, sig: usize, val: usize) -> Result<()> { +pub fn posix_sigqueue(pid: usize, sig: usize, arg: usize) -> Result<()> { + let siginf = RtSigInfo { + arg, + code: usize::wrapping_neg(1), // TODO: SI_QUEUE + }; match wrapper(false, || unsafe { - syscall::syscall3(syscall::SYS_SIGENQUEUE, pid, sig, val) + syscall::syscall3(syscall::SYS_SIGENQUEUE, pid, sig, addr_of!(siginf) as usize) }) { Ok(_) | Err(Error { errno: EINTR }) => Ok(()), Err(error) => Err(error), diff --git a/src/header/signal/linux.rs b/src/header/signal/linux.rs index f17a55c6c3..217c1ff076 100644 --- a/src/header/signal/linux.rs +++ b/src/header/signal/linux.rs @@ -78,3 +78,4 @@ pub const MINSIGSTKSZ: usize = 2048; pub const SIGSTKSZ: usize = 8096; pub const SI_QUEUE: i32 = -1; +pub const SI_USER: i32 = 0; diff --git a/src/header/signal/redox.rs b/src/header/signal/redox.rs index 689b9da3bf..488cc4c2cb 100644 --- a/src/header/signal/redox.rs +++ b/src/header/signal/redox.rs @@ -51,3 +51,6 @@ pub const SS_DISABLE: usize = 0x00000002; // TODO: It's just a guess based on Linux pub const MINSIGSTKSZ: usize = 2048; pub const SIGSTKSZ: usize = 8096; + +pub const SI_QUEUE: i32 = -1; +pub const SI_USER: i32 = 0; diff --git a/tests/sigqueue.c b/tests/sigqueue.c index a69df9645b..31e5a61c83 100644 --- a/tests/sigqueue.c +++ b/tests/sigqueue.c @@ -17,6 +17,7 @@ void action(int sig, siginfo_t *info, void *context) { assert(sig == THE_SIG); assert(info->si_signo == THE_SIG); assert(info->si_value.sival_int == num); + assert(info->si_code == SI_QUEUE); num++; write(1, "action\n", 7); } From f6761407e58dfa1f2341ef759792f4dc8f9c5f11 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 1 Aug 2024 19:51:30 +0200 Subject: [PATCH 09/27] Pass si_pid to all realtime signals. --- redox-rt/src/arch/x86_64.rs | 20 ++------------------ redox-rt/src/lib.rs | 22 +++++++++++++++++++--- redox-rt/src/signal.rs | 22 ++++++++++++++++++---- redox-rt/src/sys.rs | 9 ++++++++- src/ld_so/start.rs | 5 ++++- src/platform/mod.rs | 8 +++++--- src/platform/redox/mod.rs | 2 +- src/platform/redox/signal.rs | 1 + tests/sigqueue.c | 6 ++++++ 9 files changed, 64 insertions(+), 31 deletions(-) diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs index d918a18189..6b9849e6ce 100644 --- a/redox-rt/src/arch/x86_64.rs +++ b/redox-rt/src/arch/x86_64.rs @@ -97,11 +97,7 @@ unsafe extern "sysv64" fn fork_impl(initial_rsp: *mut usize) -> usize { unsafe extern "sysv64" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize) { 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(FdGuard::new(new_pid_fd)); } asmfunction!(__relibc_internal_fork_wrapper -> usize: [" @@ -425,11 +421,7 @@ extern "C" { fn __relibc_internal_sigentry_crit_third(); } /// Fixes some edge cases, and calculates the value for uc_stack. -pub unsafe fn arch_pre( - stack: &mut SigStack, - area: &mut SigArea, - targeted_thread: bool, -) -> PosixStackt { +pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt { // It is impossible to update RSP and RIP atomically on x86_64, without using IRETQ, which is // almost as slow as calling a SIGRETURN syscall would be. Instead, we abuse the fact that // signals are disabled in the prologue of the signal trampoline, which allows us to emulate @@ -451,14 +443,6 @@ pub unsafe fn arch_pre( stack.regs.rip = area.tmp_rip; } - stack.sig_code = if (stack.sig_num - 1) / 32 == 1 && !targeted_thread { - area.tmp_inf.code as u32 - } else { - // TODO: SIGCHLD information when applicable - 0 - }; - stack.sival = area.tmp_inf.arg; - PosixStackt { sp: stack.regs.rsp as *mut (), size: 0, // TODO diff --git a/redox-rt/src/lib.rs b/redox-rt/src/lib.rs index 106fadc264..fa25e97a93 100644 --- a/redox-rt/src/lib.rs +++ b/redox-rt/src/lib.rs @@ -9,7 +9,7 @@ )] #![forbid(unreachable_patterns)] -use core::cell::UnsafeCell; +use core::cell::{SyncUnsafeCell, UnsafeCell}; use generic_rt::{ExpectTlsFree, GenericTcb}; use syscall::{Sigcontrol, O_CLOEXEC}; @@ -120,13 +120,13 @@ pub unsafe fn tcb_activate(tcb: &RtTcb, tls_end_and_tcb_start: usize, _tls_len: } /// Initialize redox-rt in situations where relibc is not used -pub fn initialize_freestanding() { +pub unsafe fn initialize_freestanding() { // 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. // TODO: TLS - let page = unsafe { + let page = { &mut *(syscall::fmap( !0, &syscall::Map { @@ -155,4 +155,20 @@ pub fn initialize_freestanding() { let abi_ptr = core::ptr::addr_of_mut!(page.tcb_ptr); core::arch::asm!("msr tpidr_el0, {}", in(reg) abi_ptr); } + initialize(); +} +pub unsafe fn initialize() { + THIS_PID + .get() + .write(Some(syscall::getpid().unwrap().try_into().unwrap()).unwrap()); +} + +static THIS_PID: SyncUnsafeCell = SyncUnsafeCell::new(0); + +unsafe fn child_hook_common(new_pid_fd: FdGuard) { + // TODO: Currently pidfd == threadfd, but this will not be the case later. + RtTcb::current().thr_fd.get().write(Some(new_pid_fd)); + THIS_PID + .get() + .write(Some(syscall::getpid().unwrap().try_into().unwrap()).unwrap()); } diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 300564c1fd..3573b03266 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -81,13 +81,27 @@ unsafe fn inner(stack: &mut SigStack) { // asm counts from 0 stack.sig_num += 1; - stack.old_stack = arch_pre(stack, &mut *os.arch.get(), targeted_thread_not_process); + + let (sender_pid, sender_uid) = { + let area = &mut *os.arch.get(); + + stack.sival = area.tmp_inf.arg; + stack.old_stack = arch_pre(stack, area); + + if (stack.sig_num - 1) / 32 == 1 && !targeted_thread_not_process { + stack.sig_code = area.tmp_inf.code as u32; + (area.tmp_inf.pid, area.tmp_inf.uid) + } else { + // TODO: SIGCHLD information when applicable + stack.sig_code = 0; + (0, 0) // TODO + } + }; let sigaction = { let guard = SIGACTIONS_LOCK.lock(); let action = convert_old(&PROC_CONTROL_STRUCT.actions[stack.sig_num as usize - 1]); if action.flags.contains(SigactionFlags::RESETHAND) { - // TODO: other things that must be set drop(guard); sigaction( stack.sig_num as u8, @@ -159,9 +173,9 @@ unsafe fn inner(stack: &mut SigStack) { si_addr: core::ptr::null_mut(), si_code: stack.sig_code as i32, si_errno: 0, - si_pid: 0, // TODO + si_pid: sender_pid as i32, si_status: 0, - si_uid: 0, // TODO + si_uid: sender_uid as i32, si_value: stack.sival, }; sigaction( diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs index 78fa0879e9..45779f6b89 100644 --- a/redox-rt/src/sys.rs +++ b/redox-rt/src/sys.rs @@ -49,7 +49,9 @@ pub fn posix_kill(pid: usize, sig: usize) -> Result<()> { pub fn posix_sigqueue(pid: usize, sig: usize, arg: usize) -> Result<()> { let siginf = RtSigInfo { arg, - code: usize::wrapping_neg(1), // TODO: SI_QUEUE + 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) @@ -59,6 +61,11 @@ pub fn posix_sigqueue(pid: usize, sig: usize, arg: usize) -> Result<()> { } } #[inline] +pub fn posix_getpid() -> u32 { + // SAFETY: read-only except during program/fork child initialization + unsafe { crate::THIS_PID.get().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(()), diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index 6e2b45426f..d0cf2c942f 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -141,6 +141,7 @@ 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 { // We get the arguments, the environment, and the auxilary vector @@ -184,7 +185,9 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) -> } // TODO: Fix memory leak, although minimal. - crate::platform::init(auxv.clone()); + unsafe { + crate::platform::init(auxv.clone()); + } // Some variables that will be overridden by environment and auxiliary vectors let ld_library_path = envs.get("LD_LIBRARY_PATH").map(|s| s.to_owned()); diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 2b4071dd84..9fd855a7da 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -283,15 +283,17 @@ pub fn get_auxv(auxvs: &[[usize; 2]], key: usize) -> Option { #[cold] #[cfg(target_os = "redox")] -pub fn init(auxvs: Box<[[usize; 2]]>) { +// 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::*; if let (Some(cwd_ptr), Some(cwd_len)) = ( get_auxv(&auxvs, AT_REDOX_INITIAL_CWD_PTR), get_auxv(&auxvs, AT_REDOX_INITIAL_CWD_LEN), ) { - let cwd_bytes: &'static [u8] = - unsafe { core::slice::from_raw_parts(cwd_ptr as *const u8, cwd_len) }; + let cwd_bytes: &'static [u8] = core::slice::from_raw_parts(cwd_ptr as *const u8, cwd_len); if let Ok(cwd) = core::str::from_utf8(cwd_bytes) { self::sys::path::set_cwd_manual(cwd.into()); } diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index 95c7b89847..a3bda34315 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -470,7 +470,7 @@ impl Pal for Sys { } fn getpid() -> pid_t { - e(syscall::getpid()) as pid_t + redox_rt::sys::posix_getpid() as pid_t } fn getppid() -> pid_t { diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs index 3be1da8441..abcb486ce5 100644 --- a/src/platform/redox/signal.rs +++ b/src/platform/redox/signal.rs @@ -39,6 +39,7 @@ pub struct ucontext_t { } const _: () = { + #[track_caller] const fn assert_eq(a: usize, b: usize) { if a != b { panic!("compile-time struct verification failed"); diff --git a/tests/sigqueue.c b/tests/sigqueue.c index 31e5a61c83..f44322081d 100644 --- a/tests/sigqueue.c +++ b/tests/sigqueue.c @@ -12,12 +12,15 @@ volatile sig_atomic_t num = 1; +int parent; + void action(int sig, siginfo_t *info, void *context) { (void)context; assert(sig == THE_SIG); assert(info->si_signo == THE_SIG); assert(info->si_value.sival_int == num); assert(info->si_code == SI_QUEUE); + assert(info->si_pid == parent); num++; write(1, "action\n", 7); } @@ -28,6 +31,9 @@ int main(void) { status = pipe(fds); ERROR_IF(pipe, status, == -1); + parent = getpid(); + assert(parent != 0); + int child = fork(); ERROR_IF(fork, child, == -1); From d0db6a1ce07fcac9f18f85e9e12211597a3547cd Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 1 Aug 2024 20:25:20 +0200 Subject: [PATCH 10/27] Fix i686. --- redox-rt/src/arch/i686.rs | 19 +++++++------------ redox-rt/src/arch/x86_64.rs | 2 +- redox-rt/src/signal.rs | 2 +- src/platform/redox/signal.rs | 5 +++-- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/redox-rt/src/arch/i686.rs b/redox-rt/src/arch/i686.rs index 67213b9828..9bf7b73ffd 100644 --- a/redox-rt/src/arch/i686.rs +++ b/redox-rt/src/arch/i686.rs @@ -22,7 +22,7 @@ pub struct SigArea { pub tmp_eax: usize, pub tmp_ecx: usize, pub tmp_edx: usize, - pub tmp_ptr: usize, + pub tmp_inf: RtSigInfo, pub pctl: usize, // TODO: reference pctl directly pub disable_signals_depth: u64, pub last_sig_was_restart: bool, @@ -83,11 +83,7 @@ unsafe extern "cdecl" fn fork_impl(initial_rsp: *mut usize) -> usize { unsafe extern "cdecl" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize) { 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(FdGuard::new(new_pid_fd)); } asmfunction!(__relibc_internal_fork_wrapper -> usize: [" @@ -176,7 +172,7 @@ asmfunction!(__relibc_internal_sigentry: [" lea ecx, [eax+32] mov eax, {SYS_SIGDEQUEUE} mov edx, gs:[0] - add edx, {tcb_sa_off} + {sa_tmp_ptr} + add edx, {tcb_sa_off} + {sa_tmp_inf} int 0x80 mov ebx, edx test eax, eax @@ -221,15 +217,14 @@ asmfunction!(__relibc_internal_sigentry: [" sub esp, 2 * 4 + 29 * 16 fxsave [esp] - push eax - push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ptr}] - sub esp, 24 + mov [esp - 4], eax + sub esp, 48 mov ecx, esp call {inner} fxrstor [esp + 32] - add esp, 32 + 29 * 16 + 2 * 4 + add esp, 48 + 29 * 16 + 2 * 4 pop ebp pop esi @@ -274,7 +269,7 @@ __relibc_internal_sigentry_crit_third: sa_tmp_eax = const offset_of!(SigArea, tmp_eax), sa_tmp_ecx = const offset_of!(SigArea, tmp_ecx), sa_tmp_edx = const offset_of!(SigArea, tmp_edx), - sa_tmp_ptr = const offset_of!(SigArea, tmp_ptr), + sa_tmp_inf = const offset_of!(SigArea, tmp_inf), 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), diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs index 6b9849e6ce..bb757638bb 100644 --- a/redox-rt/src/arch/x86_64.rs +++ b/redox-rt/src/arch/x86_64.rs @@ -298,7 +298,7 @@ asmfunction!(__relibc_internal_sigentry: [" vextractf128 [rsp + 16], ymm14, 1 vextractf128 [rsp], ymm15, 1 5: - mov [rsp - 8], eax + mov [rsp - 4], eax sub rsp, 64 // alloc space for ucontext fields mov rdi, rsp diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 3573b03266..83768a2963 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -37,8 +37,8 @@ pub struct SigStack { pub old_stack: PosixStackt, pub old_mask: u64, pub(crate) sival: usize, - pub(crate) sig_num: u32, pub(crate) sig_code: u32, + pub(crate) sig_num: u32, // x86_64: 864 bytes // i686: 512 bytes diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs index abcb486ce5..367c49bd4f 100644 --- a/src/platform/redox/signal.rs +++ b/src/platform/redox/signal.rs @@ -28,13 +28,14 @@ pub struct ucontext_t { _pad: [usize; 1], // pad from 7*8 to 64 #[cfg(target_arch = "x86")] - _pad: [usize; 0], // don't pad from 8*4 + _pad: [usize; 3], // pad from 9*4 to 12*4 pub uc_link: *mut ucontext_t, pub uc_stack: stack_t, pub uc_sigmask: sigset_t, _sival: usize, - _signum: usize, + _sigcode: u32, + _signum: u32, pub uc_mcontext: mcontext_t, } From e5136e66c123d88899ef2f9a97985e097a183961 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 2 Aug 2024 11:47:59 +0200 Subject: [PATCH 11/27] Draft of updated aarch64 signal trampoline. --- redox-rt/src/arch/aarch64.rs | 118 ++++++++++++++++++++++++++++++----- 1 file changed, 102 insertions(+), 16 deletions(-) diff --git a/redox-rt/src/arch/aarch64.rs b/redox-rt/src/arch/aarch64.rs index 02ac534153..e6bf9827c0 100644 --- a/redox-rt/src/arch/aarch64.rs +++ b/redox-rt/src/arch/aarch64.rs @@ -1,10 +1,10 @@ -use core::mem::offset_of; +use core::{mem::offset_of, ptr::NonNull}; use syscall::{data::*, error::*}; use crate::{ proc::{fork_inner, FdGuard}, - signal::{inner_c, RtSigarea, SigStack, PROC_CONTROL_STRUCT}, + signal::{inner_c, PosixStackt, RtSigarea, SigStack, PROC_CONTROL_STRUCT}, RtTcb, Tcb, }; @@ -19,11 +19,14 @@ pub struct SigArea { pub altstack_bottom: usize, pub tmp_x1_x2: [usize; 2], pub tmp_x3_x4: [usize; 2], + pub tmp_x5_x6: [usize; 2], pub tmp_sp: usize, pub onstack: u64, pub disable_signals_depth: u64, pub pctl: usize, // TODO: remove pub last_sig_was_restart: bool, + pub last_sigstack: Option>, + pub tmp_inf: RtSigInfo, } #[repr(C)] #[derive(Debug, Default)] @@ -150,32 +153,92 @@ asmfunction!(__relibc_internal_fork_ret: [" ret "] <= [child_hook = sym child_hook]); +// https://devblogs.microsoft.com/oldnewthing/20220811-00/?p=106963 asmfunction!(__relibc_internal_sigentry: [" - // old pc and x0 are saved in the sigcontrol struct + // Clear any active reservation. + clrex + + // The old pc and x0 are saved in the sigcontrol struct. mrs x0, tpidr_el0 // ABI ptr ldr x0, [x0] // TCB ptr - // save x1-x3 and sp + // Save x1-x6 and sp 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}] mov x1, sp str x1, [x0, #{tcb_sa_off} + {sa_tmp_sp}] - sub x1, x1, 128 - and x1, x1, -16 + // Calculate new sp wrt redzone and alignment + sub x1, x1, {REDZONE_SIZE} + and x1, x1, -{STACK_ALIGN} mov sp, x1 - ldr x3, [x0, #{tcb_sa_off} + {sa_pctl}] - - // load x1 and x2 with each word (tearing between x1 and x2 can occur) + ldr x6, [x0, #{tcb_sa_off} + {sa_pctl}] +1: + // Load x1 and x2 with each signal group's bits (tearing between x1 and x2 can occur) with // acquire ordering add x2, x0, #{tcb_sc_off} + {sc_word} ldaxp x1, x2, [x2] - // reduce them by ANDing the upper and lower 32 bits - and x1, x1, x1, lsr #32 // put result in lo half - and x2, x2, x2, lsl #32 // put result in hi half - orr x1, x1, x2 // combine them into the set of pending unblocked + // First check if there are standard thread signals, + and x4, x1, x1, lsr #32 // x4 := x1 & (x1 >> 32) + cbnz x4, 3f // jump if x4 != 0 + + // and if not, load process pending bitset. + add x3, x6, #{pctl_pending} + ldaxr x3, [x3] + + // Check if there are standard proc signals: + lsr x4, x1, #32 // mask + and w4, w4, w3 // pending unblocked proc + cbz w4, 4f // skip 'fetch_andn' step if zero + + // If there was one, find which one, and try clearing the bit (last value in x3, addr in x6) + // this picks the MSB rather than the LSB, unlike x86. POSIX does not require any specific + // ordering though. + clz x4, x4 + mov x5, #32 + sub x4, x5, x4 + + mov x5, #1 + lsl x5, x5, x4 // bit to remove + + sub x5, x3, x5 // bit was certainly set, so sub is allowed + add x3, x6, #{pctl_pending} + + // Try clearing the bit, retrying on failure. + add x3, x6, #{pctl_pending} + stxr w1, x5, [x3] // try setting [x3] to x5, set x5 := 0 on success + cbnz x1, 1b + mov x1, x4 + b 2f +4: + // Check for realtime signals, thread/proc. x1 is now free real estate (but needs to contain + // the selected signal number when entering Rust). + + b . + + mov w1, w2 + orr w1, w1, w3 + and x1, x1, x1, lsr #32 + + rbit x1, x1 + clz x1, x1 + mov x2, #32 + sub x1, x2, x1 + mov x2, #1 + lsl x2, x2, x1 + + mov x5, x0 + mov x4, x8 + mov x8, {SYS_SIGDEQUEUE} + // x1 contains signal + add x2, x0, #{tcb_sa_off} + {sa_tmp_inf} + svc 0 + cbnz x0, 1b + mov x0, x5 + mov x8, x4 // count trailing zeroes, to find signal bit rbit x1, x1 @@ -183,12 +246,18 @@ asmfunction!(__relibc_internal_sigentry: [" mov x2, #32 sub x1, x2, x1 - // TODO: NOT ATOMIC! + ldr x3, [x0, #{tcb_sa_off} + {sa_pctl}] + add x2, x2, {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] // skip sigaltstack step if SA_ONSTACK is clear - // tbz x2, #57, 2f + // tbz x2, #{SA_ONSTACK_BIT}, 2f +3: + clz x1, x1 + mov x2, #32 + 64 + sub x1, x2, x1 2: ldr x2, [x0, #{tcb_sc_off} + {sc_saved_pc}] ldr x3, [x0, #{tcb_sc_off} + {sc_saved_x0}] @@ -202,7 +271,9 @@ asmfunction!(__relibc_internal_sigentry: [" stp x2, x3, [sp], #-16 ldp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}] stp x4, x3, [sp], #-16 + ldp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}] stp x6, x5, [sp], #-16 + stp x8, x7, [sp], #-16 stp x10, x9, [sp], #-16 stp x12, x11, [sp], #-16 @@ -246,16 +317,25 @@ asmfunction!(__relibc_internal_sigentry: [" ldp x18, x0, [x0] br x18 "] <= [ + pctl_pending = const (offset_of!(SigProcControl, pending)), + pctl_actions = const (offset_of!(SigProcControl, actions)), tcb_sc_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control)), tcb_sa_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch)), 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_sp = const offset_of!(SigArea, tmp_sp), + sa_tmp_inf = const offset_of!(SigArea, tmp_inf), sa_pctl = const offset_of!(SigArea, pctl), sc_saved_pc = const offset_of!(Sigcontrol, saved_ip), sc_saved_x0 = const offset_of!(Sigcontrol, saved_archdep_reg), sc_word = const offset_of!(Sigcontrol, word), inner = sym inner_c, + + SA_ONSTACK_BIT = const 58, // (1 << 58) >> 32 = 0x0400_0000 + SYS_SIGDEQUEUE = const syscall::SYS_SIGDEQUEUE, + STACK_ALIGN = const 16, + REDZONE_SIZE = const 128, ]); asmfunction!(__relibc_internal_rlct_clone_ret: [" @@ -294,4 +374,10 @@ pub unsafe fn manually_enter_trampoline() { ", inout("x0") ip_location => _, out("lr") _); } -pub unsafe fn arch_pre(stack: &mut SigStack, os: &mut SigArea) {} +pub unsafe fn arch_pre(stack: &mut SigStack, os: &mut SigArea) -> PosixStackt { + PosixStackt { + sp: core::ptr::null_mut(), // TODO + size: 0, // TODO + flags: 0, // TODO + } +} From 30caf55429e4ff00154df929ade4c38842ba9926 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 2 Aug 2024 16:01:46 +0200 Subject: [PATCH 12/27] Set siginfo_t.si_{pid,uid} from kill signals too. --- include/bits/signal.h | 2 ++ redox-rt/src/arch/x86_64.rs | 15 ++++++++--- redox-rt/src/signal.rs | 50 +++++++++++++++++++----------------- src/header/signal/redox.rs | 38 +++++++++++++++++++++++++++ src/platform/redox/signal.rs | 31 ++-------------------- tests/sigaction.c | 32 ++++++++++++++++++----- tests/sigqueue.c | 2 ++ 7 files changed, 109 insertions(+), 61 deletions(-) diff --git a/include/bits/signal.h b/include/bits/signal.h index 013540fd5f..56ffb0ec62 100644 --- a/include/bits/signal.h +++ b/include/bits/signal.h @@ -7,6 +7,8 @@ typedef struct siginfo siginfo_t; typedef unsigned long long sigset_t; +typedef struct ucontext ucontext_t; +typedef struct mcontext mcontext_t; struct sigaction { union { diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs index bb757638bb..eca4991f41 100644 --- a/redox-rt/src/arch/x86_64.rs +++ b/redox-rt/src/arch/x86_64.rs @@ -29,7 +29,8 @@ pub struct SigArea { pub tmp_rdx: usize, pub tmp_rdi: usize, pub tmp_rsi: usize, - pub tmp_inf: RtSigInfo, + pub tmp_rt_inf: RtSigInfo, + pub tmp_id_inf: u64, pub altstack_top: usize, pub altstack_bottom: usize, @@ -192,7 +193,10 @@ asmfunction!(__relibc_internal_sigentry: [" and eax, edx bsf eax, eax jz 8f + lea rdi, [rip + {pctl} + {pctl_off_sender_infos}] + mov rdi, [rdi + rax * 8] lock btr [rip + {pctl} + {pctl_off_pending}], eax + mov fs:[{tcb_sa_off} + {sa_tmp_id_inf}], rdi jc 9f 8: // Read second signal word - both process and thread simultaneously. @@ -211,7 +215,7 @@ asmfunction!(__relibc_internal_sigentry: [" mov esi, eax mov eax, {SYS_SIGDEQUEUE} mov rdi, fs:[0] - add rdi, {tcb_sa_off} + {sa_tmp_inf} // out pointer of dequeued realtime sig + add rdi, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig syscall test eax, eax jnz 1b // assumes error can only be EAGAIN @@ -220,7 +224,9 @@ asmfunction!(__relibc_internal_sigentry: [" 2: mov edx, eax shr edx, 5 + mov rdi, fs:[{tcb_sc_off} + {sc_sender_infos} + eax * 8] lock btr fs:[{tcb_sc_off} + {sc_word} + edx * 4], eax + mov fs:[{tcb_sa_off} + {sa_tmp_id_inf}], rdi add eax, 64 // indicate signal was targeted at thread 9: sub rsp, {REDZONE_SIZE} @@ -396,17 +402,20 @@ __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_inf = const offset_of!(SigArea, tmp_inf), + 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), sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom), sc_saved_rflags = const offset_of!(Sigcontrol, saved_archdep_reg), sc_saved_rip = const offset_of!(Sigcontrol, saved_ip), sc_word = const offset_of!(Sigcontrol, word), + sc_sender_infos = const offset_of!(Sigcontrol, sender_infos), sc_control = const offset_of!(Sigcontrol, control_flags), tcb_sa_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch), tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control), pctl_off_actions = const offset_of!(SigProcControl, actions), pctl_off_pending = const offset_of!(SigProcControl, pending), + pctl_off_sender_infos = const offset_of!(SigProcControl, sender_infos), pctl = sym PROC_CONTROL_STRUCT, supports_avx = sym SUPPORTS_AVX, REDZONE_SIZE = const 128, diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 83768a2963..6a8b484bae 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -6,10 +6,10 @@ use core::{ }; use syscall::{ - data::AtomicU64, Error, NonatomicUsize, RawAction, Result, SetSighandlerData, SigProcControl, - Sigcontrol, SigcontrolFlags, EINVAL, ENOMEM, EPERM, SIGABRT, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, - SIGILL, SIGKILL, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTRAP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, - SIGWINCH, SIGXCPU, SIGXFSZ, + data::AtomicU64, Error, NonatomicUsize, RawAction, Result, SenderInfo, SetSighandlerData, + SigProcControl, Sigcontrol, SigcontrolFlags, EINVAL, ENOMEM, EPERM, SIGABRT, SIGBUS, SIGCHLD, + SIGCONT, SIGFPE, SIGILL, SIGKILL, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTRAP, SIGTSTP, SIGTTIN, + SIGTTOU, SIGURG, SIGWINCH, SIGXCPU, SIGXFSZ, }; use crate::{arch::*, proc::FdGuard, sync::Mutex, RtTcb, Tcb}; @@ -85,16 +85,19 @@ unsafe fn inner(stack: &mut SigStack) { let (sender_pid, sender_uid) = { let area = &mut *os.arch.get(); - stack.sival = area.tmp_inf.arg; + // Undefined if the signal was not realtime + stack.sival = area.tmp_rt_inf.arg; + stack.old_stack = arch_pre(stack, area); if (stack.sig_num - 1) / 32 == 1 && !targeted_thread_not_process { - stack.sig_code = area.tmp_inf.code as u32; - (area.tmp_inf.pid, area.tmp_inf.uid) + stack.sig_code = area.tmp_rt_inf.code as u32; + (area.tmp_rt_inf.pid, area.tmp_rt_inf.uid) } else { - // TODO: SIGCHLD information when applicable - stack.sig_code = 0; - (0, 0) // TODO + stack.sig_code = 0; // TODO: SI_USER constant? + // TODO: Handle SIGCHLD. Maybe that should always be queued though? + let inf = SenderInfo::from_raw(area.tmp_id_inf); + (inf.pid, inf.ruid) } }; @@ -140,7 +143,6 @@ unsafe fn inner(stack: &mut SigStack) { let sigallow_inside_lo = sigallow_inside & 0xffff_ffff; let sigallow_inside_hi = sigallow_inside >> 32; - //let _ = syscall::write(1, &alloc::format!("WORD0 {:x?}\n", os.control.word).as_bytes()); let prev_w0 = os.control.word[0].fetch_add( (sigallow_inside_lo << 32).wrapping_sub(prev_sigallow_lo << 32), Ordering::Relaxed, @@ -149,7 +151,6 @@ unsafe fn inner(stack: &mut SigStack) { (sigallow_inside_hi << 32).wrapping_sub(prev_sigallow_hi << 32), Ordering::Relaxed, ); - //let _ = syscall::write(1, &alloc::format!("WORD1 {:x?}\n", os.control.word).as_bytes()); // TODO: If sa_mask caused signals to be unblocked, deliver one or all of those first? @@ -161,13 +162,12 @@ unsafe fn inner(stack: &mut SigStack) { ); core::sync::atomic::compiler_fence(Ordering::Acquire); + stack.old_mask = ((prev_w1 >> 32) << 32) | (prev_w0 >> 32); + // Call handler, either sa_handler or sa_siginfo depending on flag. if sigaction.flags.contains(SigactionFlags::SIGINFO) && let Some(sigaction) = handler.sigaction { - stack.old_mask = ((prev_w1 >> 32) << 32) | (prev_w0 >> 32); - - //let _ = syscall::write(1, alloc::format!("SIGACTION {:p}\n", sigaction).as_bytes()); let info = SiginfoAbi { si_signo: stack.sig_num as c_int, si_addr: core::ptr::null_mut(), @@ -184,10 +184,8 @@ unsafe fn inner(stack: &mut SigStack) { stack as *mut SigStack as *mut (), ); } else if let Some(handler) = handler.handler { - //let _ = syscall::write(1, alloc::format!("HANDLER {:p}\n", handler).as_bytes()); handler(stack.sig_num as c_int); } - //let _ = syscall::write(1, alloc::format!("RETURNED HANDLER\n").as_bytes()); // Disable signals while we modify the sigmask again control_flags.store( @@ -197,25 +195,30 @@ unsafe fn inner(stack: &mut SigStack) { core::sync::atomic::compiler_fence(Ordering::Acquire); // Update allowset again. - //let _ = syscall::write(1, &alloc::format!("WORD2 {:x?}\n", os.control.word).as_bytes()); + + let new_mask = stack.old_mask; + let old_mask = (os.control.word[0].load(Ordering::Relaxed) >> 32) + | ((os.control.word[1].load(Ordering::Relaxed) >> 32) << 32); let _prev_w0 = os.control.word[0].fetch_add( - (prev_sigallow_lo << 32).wrapping_sub(sigallow_inside_lo << 32), + ((new_mask & 0xffff_ffff) << 32).wrapping_sub((old_mask & 0xffff_ffff) << 32), Ordering::Relaxed, ); let _prev_w1 = os.control.word[1].fetch_add( - (prev_sigallow_hi << 32).wrapping_sub(sigallow_inside_hi << 32), + ((new_mask >> 32) << 32).wrapping_sub((old_mask >> 32) << 32), Ordering::Relaxed, ); - //let _ = syscall::write(1, &alloc::format!("WORD3 {:x?}\n", os.control.word).as_bytes()); // TODO: If resetting the sigmask caused signals to be unblocked, then should they be delivered // here? And would it be possible to tail-call-optimize that? - //let _ = syscall::write(1, alloc::format!("will return to {:x?}\n", stack.regs.eip).as_bytes()); - (*os.arch.get()).last_sig_was_restart = shall_restart; + // TODO: Support setting uc_link to jump back to a different context? + (*os.arch.get()).last_sigstack = NonNull::new(stack.link); + + // TODO: Support restoring uc_stack? + // And re-enable them again if !signals_were_disabled { core::sync::atomic::compiler_fence(Ordering::Release); @@ -502,6 +505,7 @@ pub(crate) static PROC_CONTROL_STRUCT: SigProcControl = SigProcControl { user_data: AtomicU64::new(0), } }; 64], + sender_infos: [const { AtomicU64::new(0) }; 32], }; fn combine_allowset([lo, hi]: [u64; 2]) -> u64 { diff --git a/src/header/signal/redox.rs b/src/header/signal/redox.rs index 488cc4c2cb..3ed2033f55 100644 --- a/src/header/signal/redox.rs +++ b/src/header/signal/redox.rs @@ -1,5 +1,7 @@ use core::arch::global_asm; +use super::{sigset_t, stack_t}; + pub const SIGHUP: usize = 1; pub const SIGINT: usize = 2; pub const SIGQUIT: usize = 3; @@ -54,3 +56,39 @@ pub const SIGSTKSZ: usize = 8096; pub const SI_QUEUE: i32 = -1; pub const SI_USER: i32 = 0; + +pub(crate) type ucontext_t = ucontext; +pub(crate) type mcontext_t = mcontext; + +#[repr(C)] +pub struct ucontext { + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] + _pad: [usize; 1], // pad from 7*8 to 64 + + #[cfg(target_arch = "x86")] + _pad: [usize; 3], // pad from 9*4 to 12*4 + + pub uc_link: *mut ucontext_t, + pub uc_stack: stack_t, + pub uc_sigmask: sigset_t, + _sival: usize, + _sigcode: u32, + _signum: u32, + pub uc_mcontext: mcontext_t, +} + +#[repr(C)] +pub struct mcontext { + #[cfg(target_arch = "x86")] + _opaque: [u8; 512], + #[cfg(target_arch = "x86-64")] + _opaque: [u8; 864], + #[cfg(target_arch = "aarch64")] + _opaque: [u8; 272], +} +#[no_mangle] +pub extern "C" fn __completely_unused_cbindgen_workaround_fn_ucontext_mcontext( + a: *const ucontext_t, + b: *const mcontext_t, +) { +} diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs index 367c49bd4f..f9aed39380 100644 --- a/src/platform/redox/signal.rs +++ b/src/platform/redox/signal.rs @@ -13,8 +13,8 @@ use crate::{ header::{ errno::{EINVAL, ENOSYS}, signal::{ - sigaction, siginfo_t, sigset_t, sigval, stack_t, SA_SIGINFO, SIG_BLOCK, SIG_DFL, - SIG_IGN, SIG_SETMASK, SIG_UNBLOCK, SS_DISABLE, SS_ONSTACK, + sigaction, siginfo_t, sigset_t, sigval, stack_t, ucontext_t, SA_SIGINFO, SIG_BLOCK, + SIG_DFL, SIG_IGN, SIG_SETMASK, SIG_UNBLOCK, SS_DISABLE, SS_ONSTACK, }, sys_time::{itimerval, ITIMER_REAL}, time::timespec, @@ -22,23 +22,6 @@ use crate::{ platform::ERRNO, }; -#[repr(C)] -pub struct ucontext_t { - #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] - _pad: [usize; 1], // pad from 7*8 to 64 - - #[cfg(target_arch = "x86")] - _pad: [usize; 3], // pad from 9*4 to 12*4 - - pub uc_link: *mut ucontext_t, - pub uc_stack: stack_t, - pub uc_sigmask: sigset_t, - _sival: usize, - _sigcode: u32, - _signum: u32, - pub uc_mcontext: mcontext_t, -} - const _: () = { #[track_caller] const fn assert_eq(a: usize, b: usize) { @@ -61,16 +44,6 @@ const _: () = { ); }; -#[repr(C)] -pub struct mcontext_t { - #[cfg(target_arch = "x86")] - _opaque: [u8; 512], - #[cfg(target_arch = "x86-64")] - _opaque: [u8; 864], - #[cfg(target_arch = "aarch64")] - _opaque: [u8; 272], -} - impl PalSignal for Sys { unsafe fn getitimer(which: c_int, out: *mut itimerval) -> c_int { let path = match which { diff --git a/tests/sigaction.c b/tests/sigaction.c index 245ad3b7cc..b7ad9c12c6 100644 --- a/tests/sigaction.c +++ b/tests/sigaction.c @@ -1,4 +1,6 @@ +#include #include +#include #include #include #include @@ -7,23 +9,41 @@ #include "test_helpers.h" void handler1(int sig) { - ERROR_IF(handler, sig, != SIGUSR1); - puts("Signal handler1 called!"); + assert(sig == SIGUSR1); + char *str = "Signal handler1 called!\n"; + write(STDOUT_FILENO, str, strlen(str)); } -void handler2(int sig) { - ERROR_IF(handler, sig, != SIGUSR1); - puts("Signal handler2 called!"); +sigset_t the_set = { 0 }; + +void handler2(int sig, siginfo_t *info, void *context_raw) { + assert(sig == SIGUSR1); + char *str = "Signal handler2 called!\n"; + write(STDOUT_FILENO, str, strlen(str)); + + assert(info != NULL); + assert(info->si_signo == SIGUSR1); + assert(info->si_code == SI_USER); + assert(info->si_pid == getpid()); + assert(info->si_uid == getuid()); + + ucontext_t *context = context_raw; + assert(context != NULL); + assert(memcmp(&context->uc_sigmask, &the_set, sizeof(sigset_t))); + assert(context->uc_link == NULL); } int main(void) { struct sigaction sa1 = { .sa_handler = handler1 }; - struct sigaction sa2 = { .sa_handler = handler2 }; + struct sigaction sa2 = { .sa_sigaction = handler2, .sa_flags = SA_SIGINFO }; struct sigaction saold = {0}; sigemptyset(&sa1.sa_mask); sigemptyset(&sa2.sa_mask); + int status = sigprocmask(SIG_SETMASK, NULL, &the_set); + ERROR_IF(sigprocmask, status, == -1); + int rcode = sigaction(SIGUSR1, &sa1, NULL); ERROR_IF(signal, rcode, != 0); diff --git a/tests/sigqueue.c b/tests/sigqueue.c index f44322081d..7c67fb2fb0 100644 --- a/tests/sigqueue.c +++ b/tests/sigqueue.c @@ -17,6 +17,8 @@ int parent; void action(int sig, siginfo_t *info, void *context) { (void)context; assert(sig == THE_SIG); + assert(info != NULL); + assert(context != NULL); assert(info->si_signo == THE_SIG); assert(info->si_value.sival_int == num); assert(info->si_code == SI_QUEUE); From 09108634cf91bcfd497498769b975319474f1d3d Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 2 Aug 2024 16:18:24 +0200 Subject: [PATCH 13/27] Set uc_stack properly. POSIX does not appear to explicitly mention that this should be the sigaltstack, but other Linux appears to treat it as the sigaltstack. --- redox-rt/src/arch/x86_64.rs | 11 ++++---- redox-rt/src/signal.rs | 51 +++++++++++++++++++++++++++++------- src/header/signal/redox.rs | 9 +++++++ src/platform/redox/signal.rs | 22 +++++----------- 4 files changed, 62 insertions(+), 31 deletions(-) diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs index eca4991f41..56cebb8f8c 100644 --- a/redox-rt/src/arch/x86_64.rs +++ b/redox-rt/src/arch/x86_64.rs @@ -12,7 +12,10 @@ use syscall::{ use crate::{ proc::{fork_inner, FdGuard}, - signal::{inner_c, PosixStackt, RtSigarea, SigStack, PROC_CONTROL_STRUCT}, + signal::{ + get_sigaltstack, inner_c, PosixStackt, RtSigarea, SigStack, Sigaltstack, + PROC_CONTROL_STRUCT, + }, RtTcb, Tcb, }; @@ -452,11 +455,7 @@ pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt stack.regs.rip = area.tmp_rip; } - PosixStackt { - sp: stack.regs.rsp as *mut (), - size: 0, // TODO - flags: 0, // TODO - } + get_sigaltstack(area, stack.regs.rsp).into() } pub(crate) static SUPPORTS_AVX: AtomicU8 = AtomicU8::new(0); diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 6a8b484bae..938f8ce3dc 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -52,6 +52,34 @@ pub struct PosixStackt { pub size: usize, } +pub const SS_ONSTACK: usize = 1; +pub const SS_DISABLE: usize = 2; + +impl From for PosixStackt { + fn from(value: Sigaltstack) -> Self { + match value { + Sigaltstack::Disabled => PosixStackt { + sp: core::ptr::null_mut(), + size: 0, + flags: SS_DISABLE.try_into().unwrap(), + }, + Sigaltstack::Enabled { + onstack, + base, + size, + } => PosixStackt { + sp: base.cast(), + size, + flags: if onstack { + SS_ONSTACK.try_into().unwrap() + } else { + 0 + }, + }, + } + } +} + #[repr(C)] // TODO: This struct is for practical reasons locked to Linux's ABI, but avoid redefining // it here. Alternatively, check at compile time that the structs are equivalent. @@ -590,6 +618,19 @@ pub enum Sigaltstack { size: usize, }, } + +pub(crate) fn get_sigaltstack(tcb: &SigArea, sp: usize) -> Sigaltstack { + if tcb.altstack_bottom == 0 && tcb.altstack_top == usize::MAX { + Sigaltstack::Disabled + } else { + Sigaltstack::Enabled { + base: tcb.altstack_bottom as *mut (), + size: tcb.altstack_top - tcb.altstack_bottom, + onstack: (tcb.altstack_bottom..tcb.altstack_top).contains(&sp), + } + } +} + pub unsafe fn sigaltstack( new: Option<&Sigaltstack>, old_out: Option<&mut Sigaltstack>, @@ -597,15 +638,7 @@ pub unsafe fn sigaltstack( let _g = tmp_disable_signals(); let tcb = &mut *Tcb::current().unwrap().os_specific.arch.get(); - let old = if tcb.altstack_bottom == 0 && tcb.altstack_top == usize::MAX { - Sigaltstack::Disabled - } else { - Sigaltstack::Enabled { - base: tcb.altstack_bottom as *mut (), - size: tcb.altstack_top - tcb.altstack_bottom, - onstack: (tcb.altstack_bottom..tcb.altstack_top).contains(&crate::arch::current_sp()), - } - }; + let old = get_sigaltstack(tcb, crate::arch::current_sp()); if matches!(old, Sigaltstack::Enabled { onstack: true, .. }) && new != Some(&old) { return Err(Error::new(EPERM)); diff --git a/src/header/signal/redox.rs b/src/header/signal/redox.rs index 3ed2033f55..fe08477605 100644 --- a/src/header/signal/redox.rs +++ b/src/header/signal/redox.rs @@ -50,6 +50,15 @@ pub const SA_NOCLDSTOP: usize = 0x4000_0000; pub const SS_ONSTACK: usize = 0x00000001; pub const SS_DISABLE: usize = 0x00000002; +const _: () = { + if SS_ONSTACK != redox_rt::signal::SS_ONSTACK { + panic!(); + } + if SS_DISABLE != redox_rt::signal::SS_DISABLE { + panic!(); + } +}; + // TODO: It's just a guess based on Linux pub const MINSIGSTKSZ: usize = 2048; pub const SIGSTKSZ: usize = 8096; diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs index f9aed39380..de89f58455 100644 --- a/src/platform/redox/signal.rs +++ b/src/platform/redox/signal.rs @@ -1,6 +1,6 @@ use core::mem::{self, offset_of}; use redox_rt::signal::{ - SigStack, Sigaction, SigactionFlags, SigactionKind, Sigaltstack, SignalHandler, + PosixStackt, SigStack, Sigaction, SigactionFlags, SigactionKind, Sigaltstack, SignalHandler, }; use syscall::{self, Result}; @@ -235,21 +235,11 @@ impl PalSignal for Sys { redox_rt::signal::sigaltstack(new.as_ref(), old.as_mut())?; if let (Some(old_c_stack), Some(old)) = (old_c, old) { - *old_c_stack = match old { - Sigaltstack::Disabled => stack_t { - ss_sp: core::ptr::null_mut(), - ss_size: 0, - ss_flags: SS_DISABLE.try_into().unwrap(), - }, - Sigaltstack::Enabled { - onstack, - base, - size, - } => stack_t { - ss_sp: base.cast(), - ss_size: size, - ss_flags: SS_ONSTACK.try_into().unwrap(), - }, + let c_stack = PosixStackt::from(old); + *old_c_stack = stack_t { + ss_sp: c_stack.sp.cast(), + ss_size: c_stack.size, + ss_flags: c_stack.flags, }; } Ok(()) From e860a19e8fb97b8736903fb79dcc65f77935e45c Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 2 Aug 2024 17:21:19 +0200 Subject: [PATCH 14/27] WIP: Update i686 signal asm. --- redox-rt/src/arch/i686.rs | 33 ++++++++++++++++++++++++++++----- src/header/signal/redox.rs | 5 +++-- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/redox-rt/src/arch/i686.rs b/redox-rt/src/arch/i686.rs index 9bf7b73ffd..9edff0b08a 100644 --- a/redox-rt/src/arch/i686.rs +++ b/redox-rt/src/arch/i686.rs @@ -22,7 +22,9 @@ pub struct SigArea { pub tmp_eax: usize, pub tmp_ecx: usize, pub tmp_edx: usize, - pub tmp_inf: RtSigInfo, + pub tmp_rt_inf: RtSigInfo, + 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, @@ -151,6 +153,12 @@ asmfunction!(__relibc_internal_sigentry: [" jz 3f bsf eax, eax + // Read si_pid and si_uid, atomically. + movq gs:[{tcb_sa_off} + {sa_tmp_mm0}], mm0 + movq mm0, [ecx + {pctl_sender_infos} + eax * 8] + movq gs:[{tcb_sa_off} + {sa_tmp_id_inf}], mm0 + movq mm0, gs:[{tcb_sa_off} + {sa_tmp_mm0}] + // Try clearing the pending bit, otherwise retry if another thread did that first lock btr [ecx + {pctl_word}], eax jnc 1b @@ -172,7 +180,7 @@ asmfunction!(__relibc_internal_sigentry: [" lea ecx, [eax+32] mov eax, {SYS_SIGDEQUEUE} mov edx, gs:[0] - add edx, {tcb_sa_off} + {sa_tmp_inf} + add edx, {tcb_sa_off} + {sa_tmp_rt_inf} int 0x80 mov ebx, edx test eax, eax @@ -183,13 +191,24 @@ asmfunction!(__relibc_internal_sigentry: [" 8: add eax, 32 9: + // Read si_pid and si_uid, atomically. + movq gs:[{tcb_sa_off} + {sa_tmp_mm0}], mm0 + movq mm0, gs:[{tcb_sc_off} + {sc_sender_infos} + eax * 8] + movq gs:[{tcb_sa_off} + {sa_tmp_id_inf}], mm0 + movq mm0, gs:[{tcb_sa_off} + {sa_tmp_mm0}] + mov edx, eax + shr edx, 5 + mov ecx, eax + and ecx, 31 + lock btr gs:[{tcb_sc_off} + {sc_word} + edx * 8], ecx + add eax, 64 2: and esp, -{STACK_ALIGN} mov edx, eax add edx, edx - bt dword ptr [{pctl} + {pctl_off_actions} + edx * 8 + 4], 28 + bt dword ptr [{pctl} + {pctl_actions} + edx * 8 + 4], 28 jnc 4f mov edx, gs:[{tcb_sa_off} + {sa_altstack_top}] @@ -269,7 +288,9 @@ __relibc_internal_sigentry_crit_third: sa_tmp_eax = const offset_of!(SigArea, tmp_eax), sa_tmp_ecx = const offset_of!(SigArea, tmp_ecx), sa_tmp_edx = const offset_of!(SigArea, tmp_edx), - sa_tmp_inf = const offset_of!(SigArea, tmp_inf), + 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_altstack_top = const offset_of!(SigArea, altstack_top), sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom), sa_pctl = const offset_of!(SigArea, pctl), @@ -277,9 +298,11 @@ __relibc_internal_sigentry_crit_third: sc_saved_eflags = const offset_of!(Sigcontrol, saved_archdep_reg), sc_saved_eip = const offset_of!(Sigcontrol, saved_ip), sc_word = const offset_of!(Sigcontrol, word), + sc_sender_infos = const offset_of!(Sigcontrol, sender_infos), tcb_sa_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch), tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control), - pctl_off_actions = const offset_of!(SigProcControl, actions), + pctl_actions = const offset_of!(SigProcControl, actions), + pctl_sender_infos = const offset_of!(SigProcControl, sender_infos), pctl_word = const offset_of!(SigProcControl, pending), pctl = sym PROC_CONTROL_STRUCT, STACK_ALIGN = const 16, diff --git a/src/header/signal/redox.rs b/src/header/signal/redox.rs index fe08477605..93a6988263 100644 --- a/src/header/signal/redox.rs +++ b/src/header/signal/redox.rs @@ -59,8 +59,9 @@ const _: () = { } }; -// TODO: It's just a guess based on Linux +// should include both SigStack size, and some extra room for the libc handler pub const MINSIGSTKSZ: usize = 2048; + pub const SIGSTKSZ: usize = 8096; pub const SI_QUEUE: i32 = -1; @@ -90,7 +91,7 @@ pub struct ucontext { pub struct mcontext { #[cfg(target_arch = "x86")] _opaque: [u8; 512], - #[cfg(target_arch = "x86-64")] + #[cfg(target_arch = "x86_64")] _opaque: [u8; 864], #[cfg(target_arch = "aarch64")] _opaque: [u8; 272], From 9a7cfaeaf16d4c7586fe70fd490d1e85afb11b0f Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 3 Aug 2024 01:14:07 +0200 Subject: [PATCH 15/27] Simplify aarch64 asm slightly. --- redox-rt/src/arch/aarch64.rs | 99 +++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 36 deletions(-) diff --git a/redox-rt/src/arch/aarch64.rs b/redox-rt/src/arch/aarch64.rs index e6bf9827c0..d72ef9cec4 100644 --- a/redox-rt/src/arch/aarch64.rs +++ b/redox-rt/src/arch/aarch64.rs @@ -26,7 +26,8 @@ pub struct SigArea { pub pctl: usize, // TODO: remove pub last_sig_was_restart: bool, pub last_sigstack: Option>, - pub tmp_inf: RtSigInfo, + pub tmp_rt_inf: RtSigInfo, + pub tmp_id_inf: u64, } #[repr(C)] #[derive(Debug, Default)] @@ -176,65 +177,73 @@ asmfunction!(__relibc_internal_sigentry: [" ldr x6, [x0, #{tcb_sa_off} + {sa_pctl}] 1: - // Load x1 and x2 with each signal group's bits (tearing between x1 and x2 can occur) with - // acquire ordering - add x2, x0, #{tcb_sc_off} + {sc_word} - ldaxp x1, x2, [x2] + // Load x1 with the thread's bits + add x5, x0, #{tcb_sc_off} + {sc_word} + ldaxr x1, [x5] // First check if there are standard thread signals, and x4, x1, x1, lsr #32 // x4 := x1 & (x1 >> 32) cbnz x4, 3f // jump if x4 != 0 + clrex // and if not, load process pending bitset. - add x3, x6, #{pctl_pending} - ldaxr x3, [x3] + add x1, x6, #{pctl_pending} + ldaxr x2, [x1] // Check if there are standard proc signals: - lsr x4, x1, #32 // mask - and w4, w4, w3 // pending unblocked proc - cbz w4, 4f // skip 'fetch_andn' step if zero + lsr x3, x1, #32 // mask + and w3, w3, w3 // pending unblocked proc + cbz w3, 4f // skip 'fetch_andn' step if zero // If there was one, find which one, and try clearing the bit (last value in x3, addr in x6) // this picks the MSB rather than the LSB, unlike x86. POSIX does not require any specific // ordering though. - clz x4, x4 - mov x5, #32 - sub x4, x5, x4 + clz x3, x3 + mov x4, #32 + sub x3, x4, x3 + // x3 now contains the sig_idx - mov x5, #1 - lsl x5, x5, x4 // bit to remove + mov x4, #1 + lsl x4, x4, x3 // bit to remove - sub x5, x3, x5 // bit was certainly set, so sub is allowed - add x3, x6, #{pctl_pending} + sub x4, x2, x4 // bit was certainly set, so sub is allowed + // x4 is now the new mask to be set + add x5, x6, #{pctl_pending} // Try clearing the bit, retrying on failure. - add x3, x6, #{pctl_pending} - stxr w1, x5, [x3] // try setting [x3] to x5, set x5 := 0 on success - cbnz x1, 1b - mov x1, x4 + stxr w1, x4, [x1] // try setting pending set to x4, set w1 := 0 on success + cbnz x1, 1b // retry everything if this fails + mov x1, x3 b 2f 4: - // Check for realtime signals, thread/proc. x1 is now free real estate (but needs to contain - // the selected signal number when entering Rust). + // Check for realtime signals, thread/proc. + clrex - b . + // Load the pending set again. TODO: optimize this? + add x1, x6, #{pctl_pending} + ldaxr x2, [x1] + lsr x2, x2, #32 - mov w1, w2 - orr w1, w1, w3 - and x1, x1, x1, lsr #32 + add x5, x0, #{tcb_sc_off} + {sc_word} + 8 + ldar x1, [x5] - rbit x1, x1 - clz x1, x1 - mov x2, #32 - sub x1, x2, x1 - mov x2, #1 - lsl x2, x2, x1 + orr x2, x1, x2 + and x2, x2, x2, lsr #32 + + rbit x3, x2 + clz x3, x3 + mov x4, #32 + sub x2, x4, x3 + // x2 now contains sig_idx - 32 + + // If realtime signal was directed at thread, handle it as an idempotent signal. + tbnz x1, x2, 5f mov x5, x0 mov x4, x8 mov x8, {SYS_SIGDEQUEUE} // x1 contains signal - add x2, x0, #{tcb_sa_off} + {sa_tmp_inf} + add x2, x0, #{tcb_sa_off} + {sa_tmp_rt_inf} svc 0 cbnz x0, 1b mov x0, x5 @@ -254,10 +263,26 @@ asmfunction!(__relibc_internal_sigentry: [" // skip sigaltstack step if SA_ONSTACK is clear // tbz x2, #{SA_ONSTACK_BIT}, 2f + b 2f +5: + add x1, x2, 32 + b 3b 3: + // A signal was sent to this thread, try clearing its bit. clz x1, x1 - mov x2, #32 + 64 + mov x2, #32 sub x1, x2, x1 + + add x2, x0, #{tcb_sc_off} + {sc_sender_infos} + add x2, x2, w1, utxb #3 + ldar x2, [x2] + + stxr w3, x1, [x5] + cbnz w3, 1b + + str x3, [x0, #{tcb_sa_off} + {sa_tmp_id_inf}] + add x1, x1, #64 + b 2f 2: ldr x2, [x0, #{tcb_sc_off} + {sc_saved_pc}] ldr x3, [x0, #{tcb_sc_off} + {sc_saved_x0}] @@ -325,10 +350,12 @@ asmfunction!(__relibc_internal_sigentry: [" sa_tmp_x3_x4 = const offset_of!(SigArea, tmp_x3_x4), sa_tmp_x5_x6 = const offset_of!(SigArea, tmp_x5_x6), sa_tmp_sp = const offset_of!(SigArea, tmp_sp), - sa_tmp_inf = const offset_of!(SigArea, tmp_inf), + sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf), + sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf), sa_pctl = const offset_of!(SigArea, pctl), sc_saved_pc = const offset_of!(Sigcontrol, saved_ip), sc_saved_x0 = const offset_of!(Sigcontrol, saved_archdep_reg), + sc_sender_infos = const offset_of!(Sigcontrol, sender_infos), sc_word = const offset_of!(Sigcontrol, word), inner = sym inner_c, From d6396cb4e814665cf167cd76cad06319cbbf0b2c Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 3 Aug 2024 15:41:29 +0200 Subject: [PATCH 16/27] Probably complete aarch64 asm. It contains all signal logic x86_64 already has, although statistically it will certainly have a few errors. --- redox-rt/src/arch/aarch64.rs | 87 +++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/redox-rt/src/arch/aarch64.rs b/redox-rt/src/arch/aarch64.rs index d72ef9cec4..d72e969664 100644 --- a/redox-rt/src/arch/aarch64.rs +++ b/redox-rt/src/arch/aarch64.rs @@ -210,9 +210,13 @@ asmfunction!(__relibc_internal_sigentry: [" // x4 is now the new mask to be set add x5, x6, #{pctl_pending} + add x2, x5, #{pctl_sender_infos} + add x2, x2, w3, uxtb 3 + ldar x2, [x2] + // Try clearing the bit, retrying on failure. - stxr w1, x4, [x1] // try setting pending set to x4, set w1 := 0 on success - cbnz x1, 1b // retry everything if this fails + stxr w5, x4, [x1] // try setting pending set to x4, set w1 := 0 on success + cbnz w5, 1b // retry everything if this fails mov x1, x3 b 2f 4: @@ -237,24 +241,67 @@ asmfunction!(__relibc_internal_sigentry: [" // x2 now contains sig_idx - 32 // If realtime signal was directed at thread, handle it as an idempotent signal. - tbnz x1, x2, 5f + lsr x3, x1, x2 + tbnz x3, #0, 5f mov x5, x0 mov x4, x8 - mov x8, {SYS_SIGDEQUEUE} - // x1 contains signal - add x2, x0, #{tcb_sa_off} + {sa_tmp_rt_inf} + mov x8, #{SYS_SIGDEQUEUE} + mov x0, x1 + add x1, x0, #{tcb_sa_off} + {sa_tmp_rt_inf} svc 0 - cbnz x0, 1b mov x0, x5 mov x8, x4 + cbnz x0, 1b - // count trailing zeroes, to find signal bit - rbit x1, x1 + b 2f +5: + // A realtime signal was sent to this thread, try clearing its bit. + // x3 contains last rt signal word, x2 contains rt_idx + clrex + + // Calculate the absolute sig_idx + add x1, x3, 32 + + // Load si_pid and si_uid + add x2, x0, #{tcb_sc_off} + {sc_sender_infos} + add x2, x2, w1, uxtb #3 + ldar x2, [x2] + + add x3, x0, #{tcb_sc_off} + {sc_word} + 8 + ldxr x2, [x3] + + // Calculate new mask + mov x4, #1 + lsl x4, x4, x2 + sub x2, x2, x4 // remove bit + + stxr w5, x2, [x3] + cbnz w5, 1b + str x2, [x0, #{tcb_sa_off} + {sa_tmp_id_inf}] + b 2f +3: + // A standard signal was sent to this thread, try clearing its bit. clz x1, x1 mov x2, #32 sub x1, x2, x1 + // Load si_pid and si_uid + add x2, x0, #{tcb_sc_off} + {sc_sender_infos} + add x2, x2, w1, uxtb #3 + ldar x2, [x2] + + // Clear bit from mask + mov x3, #1 + lsl x3, x3, x1 + sub x4, x4, x3 + + // Try updating the mask + stxr w3, x1, [x5] + cbnz w3, 1b + + 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 x2, x3, w1, uxtb #4 // actions_base + sig_idx * sizeof Action @@ -263,27 +310,6 @@ asmfunction!(__relibc_internal_sigentry: [" // skip sigaltstack step if SA_ONSTACK is clear // tbz x2, #{SA_ONSTACK_BIT}, 2f - b 2f -5: - add x1, x2, 32 - b 3b -3: - // A signal was sent to this thread, try clearing its bit. - clz x1, x1 - mov x2, #32 - sub x1, x2, x1 - - add x2, x0, #{tcb_sc_off} + {sc_sender_infos} - add x2, x2, w1, utxb #3 - ldar x2, [x2] - - stxr w3, x1, [x5] - cbnz w3, 1b - - str x3, [x0, #{tcb_sa_off} + {sa_tmp_id_inf}] - add x1, x1, #64 - b 2f -2: ldr x2, [x0, #{tcb_sc_off} + {sc_saved_pc}] ldr x3, [x0, #{tcb_sc_off} + {sc_saved_x0}] stp x2, x3, [sp], #-16 @@ -344,6 +370,7 @@ asmfunction!(__relibc_internal_sigentry: [" "] <= [ pctl_pending = const (offset_of!(SigProcControl, pending)), pctl_actions = const (offset_of!(SigProcControl, actions)), + pctl_sender_infos = const (offset_of!(SigProcControl, sender_infos)), tcb_sc_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control)), tcb_sa_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch)), sa_tmp_x1_x2 = const offset_of!(SigArea, tmp_x1_x2), From e82ced6e4ec258dfefbd9c1d1f4171cab554ec60 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 3 Aug 2024 20:50:03 +0200 Subject: [PATCH 17/27] Fix non-edge-case signal handling on aarch64. --- redox-rt/src/arch/aarch64.rs | 95 +++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 35 deletions(-) diff --git a/redox-rt/src/arch/aarch64.rs b/redox-rt/src/arch/aarch64.rs index d72e969664..0e4dbbc76a 100644 --- a/redox-rt/src/arch/aarch64.rs +++ b/redox-rt/src/arch/aarch64.rs @@ -170,11 +170,6 @@ asmfunction!(__relibc_internal_sigentry: [" mov x1, sp str x1, [x0, #{tcb_sa_off} + {sa_tmp_sp}] - // Calculate new sp wrt redzone and alignment - sub x1, x1, {REDZONE_SIZE} - and x1, x1, -{STACK_ALIGN} - mov sp, x1 - ldr x6, [x0, #{tcb_sa_off} + {sa_pctl}] 1: // Load x1 with the thread's bits @@ -187,20 +182,20 @@ asmfunction!(__relibc_internal_sigentry: [" clrex // and if not, load process pending bitset. - add x1, x6, #{pctl_pending} - ldaxr x2, [x1] + add x5, x6, #{pctl_pending} + ldaxr x2, [x5] // Check if there are standard proc signals: lsr x3, x1, #32 // mask - and w3, w3, w3 // pending unblocked proc + and w3, w2, w3 // pending unblocked proc cbz w3, 4f // skip 'fetch_andn' step if zero // If there was one, find which one, and try clearing the bit (last value in x3, addr in x6) // this picks the MSB rather than the LSB, unlike x86. POSIX does not require any specific // ordering though. - clz x3, x3 - mov x4, #32 - sub x3, x4, x3 + clz w3, w3 + mov w4, #31 + sub w3, w4, w3 // x3 now contains the sig_idx mov x4, #1 @@ -215,8 +210,8 @@ asmfunction!(__relibc_internal_sigentry: [" ldar x2, [x2] // Try clearing the bit, retrying on failure. - stxr w5, x4, [x1] // try setting pending set to x4, set w1 := 0 on success - cbnz w5, 1b // retry everything if this fails + stxr w1, x4, [x5] // try setting pending set to x4, set w1 := 0 on success + cbnz w1, 1b // retry everything if this fails mov x1, x3 b 2f 4: @@ -233,10 +228,11 @@ asmfunction!(__relibc_internal_sigentry: [" orr x2, x1, x2 and x2, x2, x2, lsr #32 + cbz x2, 7f rbit x3, x2 clz x3, x3 - mov x4, #32 + mov x4, #31 sub x2, x4, x3 // x2 now contains sig_idx - 32 @@ -283,7 +279,7 @@ asmfunction!(__relibc_internal_sigentry: [" 3: // A standard signal was sent to this thread, try clearing its bit. clz x1, x1 - mov x2, #32 + mov x2, #31 sub x1, x2, x1 // Load si_pid and si_uid @@ -307,40 +303,53 @@ asmfunction!(__relibc_internal_sigentry: [" add x2, x3, w1, uxtb #4 // actions_base + sig_idx * sizeof Action // TODO: NOT ATOMIC (tearing allowed between regs)! ldxp x2, x3, [x2] + clrex + + // Calculate new sp wrt redzone and alignment + mov x4, sp + sub x4, x4, {REDZONE_SIZE} + and x4, x4, -{STACK_ALIGN} + mov sp, x4 // skip sigaltstack step if SA_ONSTACK is clear // tbz x2, #{SA_ONSTACK_BIT}, 2f + ldr x2, [x0, #{tcb_sc_off} + {sc_saved_pc}] ldr x3, [x0, #{tcb_sc_off} + {sc_saved_x0}] - stp x2, x3, [sp], #-16 + stp x2, x3, [sp, #-16]! ldr x2, [x0, #{tcb_sa_off} + {sa_tmp_sp}] mrs x3, nzcv - stp x2, x3, [sp], #-16 + stp x2, x3, [sp, #-16]! ldp x2, x3, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}] - stp x2, x3, [sp], #-16 + stp x2, x3, [sp, #-16]! ldp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}] - stp x4, x3, [sp], #-16 + stp x4, x3, [sp, #-16]! ldp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}] - stp x6, x5, [sp], #-16 + stp x6, x5, [sp, #-16]! - stp x8, x7, [sp], #-16 - stp x10, x9, [sp], #-16 - stp x12, x11, [sp], #-16 - stp x14, x13, [sp], #-16 - stp x16, x15, [sp], #-16 - stp x18, x17, [sp], #-16 - stp x20, x19, [sp], #-16 - stp x22, x21, [sp], #-16 - stp x24, x23, [sp], #-16 - stp x26, x25, [sp], #-16 - stp x28, x27, [sp], #-16 - stp x30, x29, [sp], #-16 + stp x8, x7, [sp, #-16]! + stp x10, x9, [sp, #-16]! + stp x12, x11, [sp, #-16]! + stp x14, x13, [sp, #-16]! + stp x16, x15, [sp, #-16]! + stp x18, x17, [sp, #-16]! + stp x20, x19, [sp, #-16]! + stp x22, x21, [sp, #-16]! + stp x24, x23, [sp, #-16]! + stp x26, x25, [sp, #-16]! + stp x28, x27, [sp, #-16]! + stp x30, x29, [sp, #-16]! + + str w1, [sp, #-4] + sub sp, sp, #64 mov x0, sp bl {inner} + add sp, sp, #64 + ldp x30, x29, [sp], #16 ldp x28, x27, [sp], #16 ldp x26, x25, [sp], #16 @@ -356,16 +365,31 @@ asmfunction!(__relibc_internal_sigentry: [" ldp x6, x5, [sp], #16 ldp x4, x3, [sp], #16 ldp x2, x1, [sp], #16 + ldr x0, [sp, #8] msr nzcv, x0 +8: // x18 is reserved by ABI as 'platform register', so clobbering it should be safe. mov x18, sp - ldr x0, [sp], #16 + ldr x0, [x18] mov sp, x0 - mov x0, x18 - ldp x18, x0, [x0] + ldp x18, x0, [x18, #16] + br x18 +7: + // Spurious signal, i.e. all bitsets were 0 at the time they were checked + clrex + + ldr x1, [x0, #{tcb_sc_off} + {sc_flags}] + and x1, x1, ~1 + str x1, [x0, #{tcb_sc_off} + {sc_flags}] + + ldp x1, x2, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}] + ldp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}] + ldp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}] + ldr x18, [x0, #{tcb_sc_off} + {sc_saved_pc}] + ldr x0, [x0, #{tcb_sc_off} + {sc_saved_x0}] br x18 "] <= [ pctl_pending = const (offset_of!(SigProcControl, pending)), @@ -384,6 +408,7 @@ asmfunction!(__relibc_internal_sigentry: [" sc_saved_x0 = const offset_of!(Sigcontrol, saved_archdep_reg), sc_sender_infos = const offset_of!(Sigcontrol, sender_infos), sc_word = const offset_of!(Sigcontrol, word), + sc_flags = const offset_of!(Sigcontrol, control_flags), inner = sym inner_c, SA_ONSTACK_BIT = const 58, // (1 << 58) >> 32 = 0x0400_0000 From 21d23092cd695f2284691d6fab5ed4a3fe5bafc9 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 4 Aug 2024 12:06:14 +0200 Subject: [PATCH 18/27] Mostly fix i686. --- redox-rt/src/arch/i686.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/redox-rt/src/arch/i686.rs b/redox-rt/src/arch/i686.rs index 9edff0b08a..71319f50d5 100644 --- a/redox-rt/src/arch/i686.rs +++ b/redox-rt/src/arch/i686.rs @@ -148,10 +148,10 @@ asmfunction!(__relibc_internal_sigentry: [" mov ecx, gs:[{tcb_sa_off} + {sa_pctl}] // Read standard signal word - for the process - mov eax, [ecx + {pctl_word}] + mov eax, [ecx + {pctl_pending}] and eax, edx - jz 3f bsf eax, eax + jz 3f // Read si_pid and si_uid, atomically. movq gs:[{tcb_sa_off} + {sa_tmp_mm0}], mm0 @@ -160,12 +160,12 @@ asmfunction!(__relibc_internal_sigentry: [" movq mm0, gs:[{tcb_sa_off} + {sa_tmp_mm0}] // Try clearing the pending bit, otherwise retry if another thread did that first - lock btr [ecx + {pctl_word}], eax + lock btr [ecx + {pctl_pending}], eax jnc 1b jmp 2f 3: // Read realtime thread and process signal word together - mov edx, [ecx + {pctl_word} + 4] + mov edx, [ecx + {pctl_pending} + 4] mov eax, gs:[{tcb_sc_off} + {sc_word} + 8] or eax, edx and eax, gs:[{tcb_sc_off} + {sc_word} + 12] @@ -242,7 +242,7 @@ asmfunction!(__relibc_internal_sigentry: [" mov ecx, esp call {inner} - fxrstor [esp + 32] + fxrstor [esp + 48] add esp, 48 + 29 * 16 + 2 * 4 pop ebp @@ -303,7 +303,7 @@ __relibc_internal_sigentry_crit_third: tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control), pctl_actions = const offset_of!(SigProcControl, actions), pctl_sender_infos = const offset_of!(SigProcControl, sender_infos), - pctl_word = const offset_of!(SigProcControl, pending), + pctl_pending = const offset_of!(SigProcControl, pending), pctl = sym PROC_CONTROL_STRUCT, STACK_ALIGN = const 16, SYS_SIGDEQUEUE = const syscall::SYS_SIGDEQUEUE, From 33f0df3b27bc47cb37101668730b10f05c353f46 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 4 Aug 2024 18:12:59 +0200 Subject: [PATCH 19/27] Deduplicate and simplify sigprocmask code. --- redox-rt/src/signal.rs | 88 +++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 52 deletions(-) diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 938f8ce3dc..73e26ba023 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -160,25 +160,14 @@ unsafe fn inner(stack: &mut SigStack) { }; // Set sigmask to sa_mask and unmark the signal as pending. - let prev_sigallow_lo = os.control.word[0].load(Ordering::Relaxed) >> 32; - let prev_sigallow_hi = os.control.word[1].load(Ordering::Relaxed) >> 32; - let prev_sigallow = prev_sigallow_lo | (prev_sigallow_hi << 32); + let prev_sigallow = get_mask_raw(&os.control.word); let mut sigallow_inside = !sigaction.mask & prev_sigallow; if !sigaction.flags.contains(SigactionFlags::NODEFER) { sigallow_inside &= !sig_bit(stack.sig_num); } - let sigallow_inside_lo = sigallow_inside & 0xffff_ffff; - let sigallow_inside_hi = sigallow_inside >> 32; - let prev_w0 = os.control.word[0].fetch_add( - (sigallow_inside_lo << 32).wrapping_sub(prev_sigallow_lo << 32), - Ordering::Relaxed, - ); - let prev_w1 = os.control.word[1].fetch_add( - (sigallow_inside_hi << 32).wrapping_sub(prev_sigallow_hi << 32), - Ordering::Relaxed, - ); + let _pending_when_sa_mask = set_mask_raw(&os.control.word, prev_sigallow, sigallow_inside); // TODO: If sa_mask caused signals to be unblocked, deliver one or all of those first? @@ -190,7 +179,7 @@ unsafe fn inner(stack: &mut SigStack) { ); core::sync::atomic::compiler_fence(Ordering::Acquire); - stack.old_mask = ((prev_w1 >> 32) << 32) | (prev_w0 >> 32); + stack.old_mask = prev_sigallow; // Call handler, either sa_handler or sa_siginfo depending on flag. if sigaction.flags.contains(SigactionFlags::SIGINFO) @@ -225,17 +214,9 @@ unsafe fn inner(stack: &mut SigStack) { // Update allowset again. let new_mask = stack.old_mask; - let old_mask = (os.control.word[0].load(Ordering::Relaxed) >> 32) - | ((os.control.word[1].load(Ordering::Relaxed) >> 32) << 32); + let old_mask = get_mask_raw(&os.control.word); - let _prev_w0 = os.control.word[0].fetch_add( - ((new_mask & 0xffff_ffff) << 32).wrapping_sub((old_mask & 0xffff_ffff) << 32), - Ordering::Relaxed, - ); - let _prev_w1 = os.control.word[1].fetch_add( - ((new_mask >> 32) << 32).wrapping_sub((old_mask >> 32) << 32), - Ordering::Relaxed, - ); + let _pending_when_restored_mask = set_mask_raw(&os.control.word, old_mask, new_mask); // TODO: If resetting the sigmask caused signals to be unblocked, then should they be delivered // here? And would it be possible to tail-call-optimize that? @@ -267,61 +248,64 @@ pub(crate) unsafe extern "fastcall" fn inner_fastcall(stack: usize) { pub fn get_sigmask() -> Result { let mut mask = 0; - modify_sigmask(Some(&mut mask), Option:: u32>::None)?; + modify_sigmask(Some(&mut mask), Option:: u64>::None)?; Ok(mask) } pub fn set_sigmask(new: Option, old: Option<&mut u64>) -> Result<()> { - modify_sigmask( - old, - new.map(move |newmask| move |_, upper| if upper { newmask >> 32 } else { newmask } as u32), - ) + modify_sigmask(old, new.map(move |newmask| move |_| newmask)) } pub fn or_sigmask(new: Option, old: Option<&mut u64>) -> Result<()> { // Parsing nightmare... :) modify_sigmask( old, - new.map(move |newmask| { - move |oldmask, upper| oldmask | if upper { newmask >> 32 } else { newmask } as u32 - }), + new.map(move |newmask| move |oldmask| oldmask | newmask), ) } pub fn andn_sigmask(new: Option, old: Option<&mut u64>) -> Result<()> { modify_sigmask( old, - new.map(move |newmask| { - move |oldmask, upper| oldmask & !if upper { newmask >> 32 } else { newmask } as u32 - }), + new.map(move |newmask| move |oldmask| oldmask & !newmask), ) } -fn modify_sigmask(old: Option<&mut u64>, op: Option u32>) -> Result<()> { +fn get_mask_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_mask_raw(words: &[AtomicU64; 2], old: u64, new: u64) -> u64 { + // 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. + let prev_w0 = words[0].fetch_add( + ((new & 0xffff_ffff) << 32).wrapping_sub((old & 0xffff_ffff) << 32), + Ordering::Relaxed, + ) & 0xffff_ffff; + let prev_w1 = words[1].fetch_add( + ((new >> 32) << 32).wrapping_sub((old >> 32) << 32), + Ordering::Relaxed, + ) & 0xffff_ffff; + + prev_w0 | (prev_w1 << 32) +} +fn modify_sigmask(old: Option<&mut u64>, op: Option u64>) -> Result<()> { let _guard = tmp_disable_signals(); let ctl = current_sigctl(); - let words = ctl.word.each_ref().map(|w| w.load(Ordering::Relaxed)); + let prev = get_mask_raw(&ctl.word); if let Some(old) = old { - *old = !combine_allowset(words); + *old = !prev; } - let Some(mut op) = op else { + let Some(op) = op else { return Ok(()); }; - let mut can_raise = 0; + let next = !op(!prev); - for i in 0..2 { - let old_allow_bits = words[i] & 0xffff_ffff_0000_0000; - let new_allow_bits = u64::from(!op(!((old_allow_bits >> 32) as u32), i == 1)) << 32; - - let old_word = ctl.word[i].fetch_add( - new_allow_bits.wrapping_sub(old_allow_bits), - Ordering::Relaxed, - ); - can_raise |= ((old_word & 0xffff_ffff) & (new_allow_bits >> 32)) << (i * 32); - } + let pending = set_mask_raw(&ctl.word, prev, next); // POSIX requires that at least one pending unblocked signal be delivered before - // pthread_sigmask returns, if there is one. Deliver the lowest-numbered one. - if can_raise != 0 { + // pthread_sigmask returns, if there is one. + if pending != 0 { unsafe { manually_enter_trampoline(); } From 8e5ddbd6547d44e4fdde5a50fb92b693f9dd16e0 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 4 Aug 2024 18:31:24 +0200 Subject: [PATCH 20/27] Implement sigsuspend. --- redox-rt/src/signal.rs | 27 +++++++++++++++++++++++---- src/header/signal/mod.rs | 2 +- src/platform/linux/signal.rs | 6 ++++-- src/platform/pal/signal.rs | 2 +- src/platform/redox/signal.rs | 19 +++++-------------- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 73e26ba023..ce2a4344db 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -657,11 +657,30 @@ pub unsafe fn sigaltstack( pub const MIN_SIGALTSTACK_SIZE: usize = 8192; -pub fn currently_pending() -> u64 { +pub fn currently_pending_blocked() -> u64 { let control = &unsafe { Tcb::current().unwrap() }.os_specific.control; let w0 = control.word[0].load(Ordering::Relaxed); let w1 = control.word[1].load(Ordering::Relaxed); - let pending_blocked_lo = w0 & !(w0 >> 32); - let pending_unblocked_hi = w1 & !(w0 >> 32); - pending_blocked_lo | (pending_unblocked_hi << 32) + let allow = (w0 >> 32) | ((w1 >> 32) << 32); + let thread_pending = (w0 & 0xffff_ffff) | ((w1 >> 32) & 0xffff_ffff); + let proc_pending = PROC_CONTROL_STRUCT.pending.load(Ordering::Relaxed); + + core::sync::atomic::fence(Ordering::Acquire); // TODO: Correct ordering? + + (thread_pending | proc_pending) & !allow +} +pub enum Unreachable {} +pub fn wait_with_mask(mask: u64) -> Result { + let mut old = 0; + set_sigmask(Some(mask), Some(&mut old))?; + let res = syscall::nanosleep( + &syscall::TimeSpec { + tv_sec: i64::MAX, + tv_nsec: 0, + }, + &mut syscall::TimeSpec::default(), + ); + set_sigmask(Some(old), None)?; + res?; + unreachable!() } diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs index 6fe93ed266..5a4e4f252c 100644 --- a/src/header/signal/mod.rs +++ b/src/header/signal/mod.rs @@ -359,7 +359,7 @@ pub unsafe extern "C" fn sigset( #[no_mangle] pub unsafe extern "C" fn sigsuspend(sigmask: *const sigset_t) -> c_int { - Sys::sigsuspend(&*sigmask) + Err(Sys::sigsuspend(&*sigmask)).or_minus_one_errno() } #[no_mangle] diff --git a/src/platform/linux/signal.rs b/src/platform/linux/signal.rs index a6ec22fb36..7d0f4990a9 100644 --- a/src/platform/linux/signal.rs +++ b/src/platform/linux/signal.rs @@ -155,8 +155,10 @@ impl PalSignal for Sys { .map(|_| ()) } - fn sigsuspend(set: &sigset_t) -> c_int { - unsafe { e(syscall!(RT_SIGSUSPEND, set as *const sigset_t, NSIG / 8)) as c_int } + fn sigsuspend(set: &sigset_t) -> Errno { + unsafe { + e_raw(syscall!(RT_SIGSUSPEND, set as *const sigset_t, NSIG / 8)).expect_err("must fail") + } } unsafe fn sigtimedwait( diff --git a/src/platform/pal/signal.rs b/src/platform/pal/signal.rs index b00525a834..690ecdf386 100644 --- a/src/platform/pal/signal.rs +++ b/src/platform/pal/signal.rs @@ -37,7 +37,7 @@ pub trait PalSignal: Pal { oset: Option<&mut sigset_t>, ) -> Result<(), Errno>; - fn sigsuspend(set: &sigset_t) -> c_int; + fn sigsuspend(set: &sigset_t) -> Errno; // always fails unsafe fn sigtimedwait(set: *const sigset_t, sig: *mut siginfo_t, tp: *const timespec) -> c_int; diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs index de89f58455..fab3e5641b 100644 --- a/src/platform/redox/signal.rs +++ b/src/platform/redox/signal.rs @@ -246,7 +246,7 @@ impl PalSignal for Sys { } fn sigpending(set: &mut sigset_t) -> Result<(), Errno> { - *set = redox_rt::signal::currently_pending(); + *set = redox_rt::signal::currently_pending_blocked(); Ok(()) } @@ -264,20 +264,11 @@ impl PalSignal for Sys { }) } - fn sigsuspend(set: &sigset_t) -> c_int { - //TODO: correct implementation - let mut oset = sigset_t::default(); - if let Err(err) = redox_rt::signal::set_sigmask(Some(*set), Some(&mut oset)) { - Errno::from(err).sync(); - return -1; + fn sigsuspend(set: &sigset_t) -> Errno { + match redox_rt::signal::wait_with_mask(*set) { + Ok(_) => unreachable!(), + Err(err) => err.into(), } - //TODO: wait for signal - Self::sched_yield(); - if let Err(err) = redox_rt::signal::set_sigmask(Some(oset), None) { - Errno::from(err).sync(); - return -1; - } - 0 } unsafe fn sigtimedwait( From 5c78356290fe6b369fd3b8a308cacbe80bcc5767 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 4 Aug 2024 19:31:06 +0200 Subject: [PATCH 21/27] Implement sigtimedwait on Redox. --- redox-rt/src/signal.rs | 152 +++++++++++++++++++++++++++++++---- src/header/signal/mod.rs | 4 +- src/header/signal/redox.rs | 13 ++- src/platform/linux/signal.rs | 17 ++-- src/platform/pal/signal.rs | 5 +- src/platform/redox/signal.rs | 23 +++--- 6 files changed, 177 insertions(+), 37 deletions(-) diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index ce2a4344db..4a8e0a675d 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -1,15 +1,17 @@ use core::{ cell::{Cell, UnsafeCell}, ffi::{c_int, c_void}, + mem::MaybeUninit, ptr::NonNull, sync::atomic::{AtomicU8, AtomicUsize, Ordering}, }; use syscall::{ - data::AtomicU64, Error, NonatomicUsize, RawAction, Result, SenderInfo, SetSighandlerData, - SigProcControl, Sigcontrol, SigcontrolFlags, EINVAL, ENOMEM, EPERM, SIGABRT, SIGBUS, SIGCHLD, - SIGCONT, SIGFPE, SIGILL, SIGKILL, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTRAP, SIGTSTP, SIGTTIN, - SIGTTOU, SIGURG, SIGWINCH, SIGXCPU, SIGXFSZ, + data::AtomicU64, Error, NonatomicUsize, RawAction, Result, RtSigInfo, SenderInfo, + SetSighandlerData, SigProcControl, Sigcontrol, SigcontrolFlags, TimeSpec, EAGAIN, EINTR, + EINVAL, ENOMEM, EPERM, SIGABRT, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, SIGILL, SIGKILL, SIGQUIT, + SIGSEGV, SIGSTOP, SIGSYS, SIGTRAP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGWINCH, SIGXCPU, + SIGXFSZ, }; use crate::{arch::*, proc::FdGuard, sync::Mutex, RtTcb, Tcb}; @@ -160,14 +162,14 @@ unsafe fn inner(stack: &mut SigStack) { }; // Set sigmask to sa_mask and unmark the signal as pending. - let prev_sigallow = get_mask_raw(&os.control.word); + let prev_sigallow = get_allowset_raw(&os.control.word); let mut sigallow_inside = !sigaction.mask & prev_sigallow; if !sigaction.flags.contains(SigactionFlags::NODEFER) { sigallow_inside &= !sig_bit(stack.sig_num); } - let _pending_when_sa_mask = set_mask_raw(&os.control.word, prev_sigallow, sigallow_inside); + let _pending_when_sa_mask = set_allowset_raw(&os.control.word, prev_sigallow, sigallow_inside); // TODO: If sa_mask caused signals to be unblocked, deliver one or all of those first? @@ -214,9 +216,9 @@ unsafe fn inner(stack: &mut SigStack) { // Update allowset again. let new_mask = stack.old_mask; - let old_mask = get_mask_raw(&os.control.word); + let old_mask = get_allowset_raw(&os.control.word); - let _pending_when_restored_mask = set_mask_raw(&os.control.word, old_mask, new_mask); + let _pending_when_restored_mask = set_allowset_raw(&os.control.word, old_mask, new_mask); // TODO: If resetting the sigmask caused signals to be unblocked, then should they be delivered // here? And would it be possible to tail-call-optimize that? @@ -267,11 +269,11 @@ pub fn andn_sigmask(new: Option, old: Option<&mut u64>) -> Result<()> { new.map(move |newmask| move |oldmask| oldmask & !newmask), ) } -fn get_mask_raw(words: &[AtomicU64; 2]) -> u64 { +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_mask_raw(words: &[AtomicU64; 2], old: u64, new: u64) -> u64 { +fn set_allowset_raw(words: &[AtomicU64; 2], old: u64, new: u64) -> u64 { // 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. @@ -290,7 +292,7 @@ fn modify_sigmask(old: Option<&mut u64>, op: Option u64>) -> let _guard = tmp_disable_signals(); let ctl = current_sigctl(); - let prev = get_mask_raw(&ctl.word); + let prev = get_allowset_raw(&ctl.word); if let Some(old) = old { *old = !prev; @@ -301,7 +303,7 @@ fn modify_sigmask(old: Option<&mut u64>, op: Option u64>) -> let next = !op(!prev); - let pending = set_mask_raw(&ctl.word, prev, next); + let pending = set_allowset_raw(&ctl.word, prev, next); // POSIX requires that at least one pending unblocked signal be delivered before // pthread_sigmask returns, if there is one. @@ -655,7 +657,7 @@ pub unsafe fn sigaltstack( Ok(()) } -pub const MIN_SIGALTSTACK_SIZE: usize = 8192; +pub const MIN_SIGALTSTACK_SIZE: usize = 2048; pub fn currently_pending_blocked() -> u64 { let control = &unsafe { Tcb::current().unwrap() }.os_specific.control; @@ -670,17 +672,133 @@ pub fn currently_pending_blocked() -> u64 { (thread_pending | proc_pending) & !allow } pub enum Unreachable {} -pub fn wait_with_mask(mask: u64) -> Result { + +pub fn await_signal_async(set: u64) -> Result { let mut old = 0; - set_sigmask(Some(mask), Some(&mut old))?; + set_sigmask(Some(!set), Some(&mut old))?; + // TODO: RAII guard let res = syscall::nanosleep( - &syscall::TimeSpec { + &TimeSpec { tv_sec: i64::MAX, tv_nsec: 0, }, - &mut syscall::TimeSpec::default(), + &mut TimeSpec::default(), ); set_sigmask(Some(old), None)?; res?; unreachable!() } +// TODO: deadline-based API +pub fn await_signal_sync(inner_allowset: u64, timeout: &TimeSpec) -> Result { + let _guard = tmp_disable_signals(); + let control = &unsafe { Tcb::current().unwrap() }.os_specific.control; + + let old_allowset = get_allowset_raw(&control.word); + let proc_pending = PROC_CONTROL_STRUCT.pending.load(Ordering::Acquire); + let thread_pending = set_allowset_raw(&control.word, old_allowset, inner_allowset); + + // Check if there are already signals matching the requested set, before waiting. + if let Some(info) = try_claim_multiple(proc_pending, thread_pending, inner_allowset, control) { + // TODO: RAII + set_allowset_raw(&control.word, inner_allowset, old_allowset); + return Ok(info); + } + + let res = syscall::nanosleep(&timeout, &mut TimeSpec::default()); + let thread_pending = set_allowset_raw(&control.word, inner_allowset, old_allowset); + let proc_pending = PROC_CONTROL_STRUCT.pending.load(Ordering::Acquire); + + if let Err(error) = res + && error.errno != EINTR + { + return Err(error); + } + + // Then check if there were any signals left after waiting. + try_claim_multiple(proc_pending, thread_pending, inner_allowset, control) + // Normally ETIMEDOUT but not for sigtimedwait. + .ok_or(Error::new(EAGAIN)) +} +fn try_claim_multiple( + mut proc_pending: u64, + mut thread_pending: u64, + allowset: u64, + control: &Sigcontrol, +) -> Option { + while (proc_pending | thread_pending) & allowset != 0 { + let sig_idx = ((proc_pending | thread_pending) & allowset).trailing_zeros(); + if thread_pending & (1 << sig_idx) != 0 + && let Some(res) = try_claim_single(sig_idx, Some(control)) + { + return Some(res); + } + thread_pending &= !(1 << sig_idx); + if let Some(res) = try_claim_single(sig_idx, None) { + return Some(res); + } + proc_pending &= !(1 << sig_idx); + } + None +} +fn try_claim_single(sig_idx: u32, thread_control: Option<&Sigcontrol>) -> Option { + let sig_group = sig_idx / 1; + + if sig_group == 1 && thread_control.is_none() { + // Queued (realtime) signal + let mut ret = MaybeUninit::::uninit(); + let rt_inf = unsafe { + syscall::syscall2( + syscall::SYS_SIGDEQUEUE, + sig_idx as usize, + ret.as_mut_ptr() as usize, + ) + .ok()?; + ret.assume_init() + }; + Some(SiginfoAbi { + si_signo: sig_idx as i32 + 1, + si_errno: 0, + si_code: rt_inf.code, + si_pid: rt_inf.pid as i32, + si_uid: rt_inf.uid as i32, + si_status: 0, + si_value: rt_inf.arg, + si_addr: core::ptr::null_mut(), + }) + } else { + // Idempotent (standard or thread realtime) signal + let info = SenderInfo::from_raw(match thread_control { + Some(ctl) => { + // Only this thread can clear pending bits, so this will always succeed. + let info = ctl.sender_infos[sig_idx as usize].load(Ordering::Acquire); + // TODO: Ordering + ctl.word[sig_group as usize].fetch_and(!(1 << (sig_idx % 32)), Ordering::Release); + info + } + None => { + let info = + PROC_CONTROL_STRUCT.sender_infos[sig_idx as usize].load(Ordering::Acquire); + if PROC_CONTROL_STRUCT + .pending + .fetch_and(!(1 << sig_idx), Ordering::Release) + & (1 << sig_idx) + == 0 + { + // already claimed + return None; + } + info + } + }); + Some(SiginfoAbi { + si_signo: sig_idx as i32 + 1, + si_errno: 0, + si_code: 0, // TODO: SI_USER const? + si_pid: info.pid as i32, + si_uid: info.ruid as i32, + si_status: 0, + si_value: 0, // undefined + si_addr: core::ptr::null_mut(), + }) + } +} diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs index 5a4e4f252c..05462424a2 100644 --- a/src/header/signal/mod.rs +++ b/src/header/signal/mod.rs @@ -379,7 +379,9 @@ pub unsafe extern "C" fn sigtimedwait( sig: *mut siginfo, // https://github.com/mozilla/cbindgen/issues/621 tp: *const timespec, ) -> c_int { - Sys::sigtimedwait(set, sig, tp) + Sys::sigtimedwait(&*set, &mut *sig, &*tp) + .map(|()| 0) + .or_minus_one_errno() } pub const _signal_strings: [&str; 32] = [ diff --git a/src/header/signal/redox.rs b/src/header/signal/redox.rs index 93a6988263..b0c7ed33fc 100644 --- a/src/header/signal/redox.rs +++ b/src/header/signal/redox.rs @@ -1,6 +1,8 @@ use core::arch::global_asm; -use super::{sigset_t, stack_t}; +use redox_rt::signal::SiginfoAbi; + +use super::{siginfo_t, sigset_t, stack_t}; pub const SIGHUP: usize = 1; pub const SIGINT: usize = 2; @@ -57,6 +59,9 @@ const _: () = { if SS_DISABLE != redox_rt::signal::SS_DISABLE { panic!(); } + if MINSIGSTKSZ != redox_rt::signal::MIN_SIGALTSTACK_SIZE { + panic!(); + } }; // should include both SigStack size, and some extra room for the libc handler @@ -102,3 +107,9 @@ pub extern "C" fn __completely_unused_cbindgen_workaround_fn_ucontext_mcontext( b: *const mcontext_t, ) { } + +impl From for siginfo_t { + fn from(value: SiginfoAbi) -> Self { + unsafe { core::mem::transmute(value) } + } +} diff --git a/src/platform/linux/signal.rs b/src/platform/linux/signal.rs index 7d0f4990a9..bc438a6853 100644 --- a/src/platform/linux/signal.rs +++ b/src/platform/linux/signal.rs @@ -161,11 +161,16 @@ impl PalSignal for Sys { } } - unsafe fn sigtimedwait( - set: *const sigset_t, - sig: *mut siginfo_t, - tp: *const timespec, - ) -> c_int { - e(syscall!(RT_SIGTIMEDWAIT, set, sig, tp, NSIG / 8)) as c_int + fn sigtimedwait(set: &sigset_t, sig: &mut siginfo_t, tp: ×pec) -> Result<(), Errno> { + unsafe { + e_raw(syscall!( + RT_SIGTIMEDWAIT, + set as *const _, + sig as *mut _, + tp as *const _, + NSIG / 8 + )) + .map(|_| ()) + } } } diff --git a/src/platform/pal/signal.rs b/src/platform/pal/signal.rs index 690ecdf386..99699f68ea 100644 --- a/src/platform/pal/signal.rs +++ b/src/platform/pal/signal.rs @@ -37,8 +37,7 @@ pub trait PalSignal: Pal { oset: Option<&mut sigset_t>, ) -> Result<(), Errno>; - fn sigsuspend(set: &sigset_t) -> Errno; // always fails + fn sigsuspend(mask: &sigset_t) -> Errno; // always fails - unsafe fn sigtimedwait(set: *const sigset_t, sig: *mut siginfo_t, tp: *const timespec) - -> c_int; + fn sigtimedwait(set: &sigset_t, sig: &mut siginfo_t, tp: ×pec) -> Result<(), Errno>; } diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs index fab3e5641b..189662cbc9 100644 --- a/src/platform/redox/signal.rs +++ b/src/platform/redox/signal.rs @@ -264,19 +264,24 @@ impl PalSignal for Sys { }) } - fn sigsuspend(set: &sigset_t) -> Errno { - match redox_rt::signal::wait_with_mask(*set) { + fn sigsuspend(mask: &sigset_t) -> Errno { + match redox_rt::signal::await_signal_async(!*mask) { Ok(_) => unreachable!(), Err(err) => err.into(), } } - unsafe fn sigtimedwait( - set: *const sigset_t, - sig: *mut siginfo_t, - tp: *const timespec, - ) -> c_int { - ERRNO.set(ENOSYS); - -1 + fn sigtimedwait( + set: &sigset_t, + info_out: &mut siginfo_t, + timeout: ×pec, + ) -> Result<(), Errno> { + // TODO: deadline-based API + let timeout = syscall::TimeSpec { + tv_sec: timeout.tv_sec, + tv_nsec: timeout.tv_nsec as _, + }; + *info_out = redox_rt::signal::await_signal_sync(*set, &timeout)?.into(); + Ok(()) } } From 9701e9c544fef77e13a4f80076d1426d29b98b81 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 4 Aug 2024 22:39:46 +0200 Subject: [PATCH 22/27] Add sigtimedwait to test, and various fixes. --- redox-rt/src/signal.rs | 10 +++++--- src/header/signal/mod.rs | 10 +++++--- src/platform/linux/signal.rs | 13 +++++++--- src/platform/pal/signal.rs | 6 ++++- src/platform/redox/signal.rs | 7 ++++-- tests/sigaction.c | 5 ++++ tests/sigqueue.c | 49 +++++++++++++++++++++++------------- 7 files changed, 69 insertions(+), 31 deletions(-) diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 4a8e0a675d..e041bfd3d7 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -727,13 +727,15 @@ fn try_claim_multiple( ) -> Option { while (proc_pending | thread_pending) & allowset != 0 { let sig_idx = ((proc_pending | thread_pending) & allowset).trailing_zeros(); - if thread_pending & (1 << sig_idx) != 0 + if thread_pending & allowset & (1 << sig_idx) != 0 && let Some(res) = try_claim_single(sig_idx, Some(control)) { return Some(res); } thread_pending &= !(1 << sig_idx); - if let Some(res) = try_claim_single(sig_idx, None) { + if proc_pending & allowset & (1 << sig_idx) != 0 + && let Some(res) = try_claim_single(sig_idx, None) + { return Some(res); } proc_pending &= !(1 << sig_idx); @@ -741,7 +743,7 @@ fn try_claim_multiple( None } fn try_claim_single(sig_idx: u32, thread_control: Option<&Sigcontrol>) -> Option { - let sig_group = sig_idx / 1; + let sig_group = sig_idx / 32; if sig_group == 1 && thread_control.is_none() { // Queued (realtime) signal @@ -749,8 +751,8 @@ fn try_claim_single(sig_idx: u32, thread_control: Option<&Sigcontrol>) -> Option let rt_inf = unsafe { syscall::syscall2( syscall::SYS_SIGDEQUEUE, - sig_idx as usize, ret.as_mut_ptr() as usize, + sig_idx as usize - 32, ) .ok()?; ret.assume_init() diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs index 05462424a2..930e66b6e7 100644 --- a/src/header/signal/mod.rs +++ b/src/header/signal/mod.rs @@ -142,7 +142,9 @@ pub unsafe extern "C" fn sigaction( #[no_mangle] pub unsafe extern "C" fn sigaddset(set: *mut sigset_t, signo: c_int) -> c_int { - if signo <= 0 || signo as usize > NSIG { + if signo <= 0 || signo as usize > NSIG.max(SIGRTMAX) + /* TODO */ + { platform::ERRNO.set(errno::EINVAL); return -1; } @@ -162,7 +164,9 @@ pub unsafe extern "C" fn sigaltstack(ss: *const stack_t, old_ss: *mut stack_t) - #[no_mangle] pub unsafe extern "C" fn sigdelset(set: *mut sigset_t, signo: c_int) -> c_int { - if signo <= 0 || signo as usize > NSIG { + if signo <= 0 || signo as usize > NSIG.max(SIGRTMAX) + /* TODO */ + { platform::ERRNO.set(errno::EINVAL); return -1; } @@ -379,7 +383,7 @@ pub unsafe extern "C" fn sigtimedwait( sig: *mut siginfo, // https://github.com/mozilla/cbindgen/issues/621 tp: *const timespec, ) -> c_int { - Sys::sigtimedwait(&*set, &mut *sig, &*tp) + Sys::sigtimedwait(&*set, sig.as_mut(), &*tp) .map(|()| 0) .or_minus_one_errno() } diff --git a/src/platform/linux/signal.rs b/src/platform/linux/signal.rs index bc438a6853..f772988a02 100644 --- a/src/platform/linux/signal.rs +++ b/src/platform/linux/signal.rs @@ -155,18 +155,23 @@ impl PalSignal for Sys { .map(|_| ()) } - fn sigsuspend(set: &sigset_t) -> Errno { + fn sigsuspend(mask: &sigset_t) -> Errno { unsafe { - e_raw(syscall!(RT_SIGSUSPEND, set as *const sigset_t, NSIG / 8)).expect_err("must fail") + e_raw(syscall!(RT_SIGSUSPEND, mask as *const sigset_t, NSIG / 8)) + .expect_err("must fail") } } - fn sigtimedwait(set: &sigset_t, sig: &mut siginfo_t, tp: ×pec) -> Result<(), Errno> { + fn sigtimedwait( + set: &sigset_t, + sig: Option<&mut siginfo_t>, + tp: ×pec, + ) -> Result<(), Errno> { unsafe { e_raw(syscall!( RT_SIGTIMEDWAIT, set as *const _, - sig as *mut _, + sig.map_or_else(core::ptr::null_mut, |s| s as *mut _) as usize, tp as *const _, NSIG / 8 )) diff --git a/src/platform/pal/signal.rs b/src/platform/pal/signal.rs index 99699f68ea..4d2dbb6ff3 100644 --- a/src/platform/pal/signal.rs +++ b/src/platform/pal/signal.rs @@ -39,5 +39,9 @@ pub trait PalSignal: Pal { fn sigsuspend(mask: &sigset_t) -> Errno; // always fails - fn sigtimedwait(set: &sigset_t, sig: &mut siginfo_t, tp: ×pec) -> Result<(), Errno>; + fn sigtimedwait( + set: &sigset_t, + sig: Option<&mut siginfo_t>, + tp: ×pec, + ) -> Result<(), Errno>; } diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs index 189662cbc9..a5f0c10509 100644 --- a/src/platform/redox/signal.rs +++ b/src/platform/redox/signal.rs @@ -273,7 +273,7 @@ impl PalSignal for Sys { fn sigtimedwait( set: &sigset_t, - info_out: &mut siginfo_t, + info_out: Option<&mut siginfo_t>, timeout: ×pec, ) -> Result<(), Errno> { // TODO: deadline-based API @@ -281,7 +281,10 @@ impl PalSignal for Sys { tv_sec: timeout.tv_sec, tv_nsec: timeout.tv_nsec as _, }; - *info_out = redox_rt::signal::await_signal_sync(*set, &timeout)?.into(); + let info = redox_rt::signal::await_signal_sync(*set, &timeout)?.into(); + if let Some(out) = info_out { + *out = info; + } Ok(()) } } diff --git a/tests/sigaction.c b/tests/sigaction.c index b7ad9c12c6..b332f3902d 100644 --- a/tests/sigaction.c +++ b/tests/sigaction.c @@ -23,14 +23,19 @@ void handler2(int sig, siginfo_t *info, void *context_raw) { assert(info != NULL); assert(info->si_signo == SIGUSR1); +#ifndef __linux + // TODO: SI_TKILL? assert(info->si_code == SI_USER); assert(info->si_pid == getpid()); assert(info->si_uid == getuid()); +#endif ucontext_t *context = context_raw; assert(context != NULL); +#ifndef __linux__ // TODO assert(memcmp(&context->uc_sigmask, &the_set, sizeof(sigset_t))); assert(context->uc_link == NULL); +#endif } int main(void) { diff --git a/tests/sigqueue.c b/tests/sigqueue.c index 7c67fb2fb0..78e4eeffd5 100644 --- a/tests/sigqueue.c +++ b/tests/sigqueue.c @@ -10,20 +10,24 @@ #define THE_SIG SIGRTMIN -volatile sig_atomic_t num = 1; +volatile sig_atomic_t num = 0; int parent; -void action(int sig, siginfo_t *info, void *context) { - (void)context; +void validate(int sig, const siginfo_t *info) { assert(sig == THE_SIG); assert(info != NULL); - assert(context != NULL); assert(info->si_signo == THE_SIG); assert(info->si_value.sival_int == num); assert(info->si_code == SI_QUEUE); assert(info->si_pid == parent); +} + +void action(int sig, siginfo_t *info, void *context) { num++; + (void)context; + assert(context != NULL); + validate(sig, info); write(1, "action\n", 7); } @@ -42,22 +46,27 @@ int main(void) { status = close(fds[child == 0 ? 0 : 1]); ERROR_IF(close, status, == -1); - sigset_t set; - status = sigfillset(&set); + sigset_t set, mask; + status = sigfillset(&mask); ERROR_IF(sigfillset, status, == -1); - status = sigdelset(&set, SIGSEGV); + status = sigdelset(&mask, SIGSEGV); ERROR_IF(sigdelset, status, == -1); - status = sigdelset(&set, SIGBUS); + status = sigdelset(&mask, SIGBUS); ERROR_IF(sigdelset, status, == -1); - status = sigdelset(&set, SIGILL); + status = sigdelset(&mask, SIGILL); ERROR_IF(sigdelset, status, == -1); - status = sigdelset(&set, SIGFPE); + status = sigdelset(&mask, SIGFPE); ERROR_IF(sigdelset, status, == -1); - status = sigdelset(&set, SIGINT); + status = sigdelset(&mask, SIGINT); ERROR_IF(sigdelset, status, == -1); - status = sigprocmask(SIG_SETMASK, &set, NULL); + status = sigprocmask(SIG_SETMASK, &mask, NULL); ERROR_IF(sigprocmask, status, == -1); + status = sigemptyset(&set); + ERROR_IF(sigemptyset, status, == -1); + status = sigaddset(&set, THE_SIG); + ERROR_IF(sigaddset, status, == -1); + struct sigaction sa; memcpy(&sa.sa_mask, &set, sizeof (sigset_t)); sa.sa_flags = SA_SIGINFO; @@ -67,14 +76,20 @@ int main(void) { ERROR_IF(sigaction, status, == -1); if (child == 0) { - status = sigemptyset(&set); - ERROR_IF(sigemptyset, status, == -1); - while (num != 32) { - } + assert(num == 0); + siginfo_t info; + struct timespec t = (struct timespec){ .tv_sec = 1, .tv_nsec = 0 }; + status = sigtimedwait(&set, &info, &t); + ERROR_IF(sigtimedwait, status, == -1); + validate(THE_SIG, &info); + assert(num == 0); // ensure no signal handler ran + + while (num < 31) {} + status = write(fds[1], "A", 1); ERROR_IF(write, status, == -1); } else { - for (int n = 1; n <= 32; n++) { + for (int n = 0; n <= 31; n++) { status = sigqueue(child, THE_SIG, (union sigval){ .sival_int = n }); ERROR_IF(sigqueue, status, == -1); } From 2b7a1ea94bf8431b084c0056eb2e7e2dfce9f9a2 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 4 Aug 2024 23:02:04 +0200 Subject: [PATCH 23/27] Fix sigsuspend and add it to the sigqueue test. --- redox-rt/src/signal.rs | 34 +++++++++++++++++++++++++++------- src/header/signal/mod.rs | 11 +++++++++-- src/platform/linux/signal.rs | 6 +++--- src/platform/pal/signal.rs | 2 +- src/platform/redox/signal.rs | 8 ++++---- tests/sigqueue.c | 32 +++++++++++++++++++++++++------- 6 files changed, 69 insertions(+), 24 deletions(-) diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index e041bfd3d7..345232cad2 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -673,10 +673,13 @@ pub fn currently_pending_blocked() -> u64 { } pub enum Unreachable {} -pub fn await_signal_async(set: u64) -> Result { - let mut old = 0; - set_sigmask(Some(!set), Some(&mut old))?; - // TODO: RAII guard +pub fn await_signal_async(inner_allowset: u64) -> Result { + let _guard = tmp_disable_signals(); + let control = &unsafe { Tcb::current().unwrap() }.os_specific.control; + + let old_allowset = get_allowset_raw(&control.word); + set_allowset_raw(&control.word, old_allowset, inner_allowset); + let res = syscall::nanosleep( &TimeSpec { tv_sec: i64::MAX, @@ -684,12 +687,19 @@ pub fn await_signal_async(set: u64) -> Result { }, &mut TimeSpec::default(), ); - set_sigmask(Some(old), None)?; + set_allowset_raw(&control.word, inner_allowset, old_allowset); + + if res == Err(Error::new(EINTR)) { + unsafe { + manually_enter_trampoline(); + } + } + res?; unreachable!() } // TODO: deadline-based API -pub fn await_signal_sync(inner_allowset: u64, timeout: &TimeSpec) -> Result { +pub fn await_signal_sync(inner_allowset: u64, timeout: Option<&TimeSpec>) -> Result { let _guard = tmp_disable_signals(); let control = &unsafe { Tcb::current().unwrap() }.os_specific.control; @@ -704,7 +714,17 @@ pub fn await_signal_sync(inner_allowset: u64, timeout: &TimeSpec) -> Result syscall::nanosleep(&t, &mut TimeSpec::default()), + None => syscall::nanosleep( + &TimeSpec { + tv_sec: i64::MAX, + tv_nsec: 0, + }, + &mut TimeSpec::default(), + ), + }; + let thread_pending = set_allowset_raw(&control.word, inner_allowset, old_allowset); let proc_pending = PROC_CONTROL_STRUCT.pending.load(Ordering::Acquire); diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs index 930e66b6e7..dbd6d69ced 100644 --- a/src/header/signal/mod.rs +++ b/src/header/signal/mod.rs @@ -380,13 +380,20 @@ pub unsafe extern "C" fn sigwait(set: *const sigset_t, sig: *mut c_int) -> c_int #[no_mangle] pub unsafe extern "C" fn sigtimedwait( set: *const sigset_t, - sig: *mut siginfo, // https://github.com/mozilla/cbindgen/issues/621 + // s/siginfo_t/siginfo due to https://github.com/mozilla/cbindgen/issues/621 + sig: *mut siginfo, + // POSIX leaves behavior unspecified if this is NULL, but on both Linux and Redox, NULL is used + // to differentiate between sigtimedwait and sigwaitinfo internally tp: *const timespec, ) -> c_int { - Sys::sigtimedwait(&*set, sig.as_mut(), &*tp) + Sys::sigtimedwait(&*set, sig.as_mut(), tp.as_ref()) .map(|()| 0) .or_minus_one_errno() } +#[no_mangle] +pub unsafe extern "C" fn sigwaitinfo(set: *const sigset_t, sig: *mut siginfo_t) -> c_int { + sigtimedwait(set, sig, core::ptr::null()) +} pub const _signal_strings: [&str; 32] = [ "Unknown signal\0", diff --git a/src/platform/linux/signal.rs b/src/platform/linux/signal.rs index f772988a02..7a379e06af 100644 --- a/src/platform/linux/signal.rs +++ b/src/platform/linux/signal.rs @@ -165,14 +165,14 @@ impl PalSignal for Sys { fn sigtimedwait( set: &sigset_t, sig: Option<&mut siginfo_t>, - tp: ×pec, + tp: Option<×pec>, ) -> Result<(), Errno> { unsafe { e_raw(syscall!( RT_SIGTIMEDWAIT, set as *const _, - sig.map_or_else(core::ptr::null_mut, |s| s as *mut _) as usize, - tp as *const _, + sig.map_or_else(core::ptr::null_mut, |s| s as *mut _), + tp.map_or_else(core::ptr::null, |t| t as *const _), NSIG / 8 )) .map(|_| ()) diff --git a/src/platform/pal/signal.rs b/src/platform/pal/signal.rs index 4d2dbb6ff3..7a01780ac1 100644 --- a/src/platform/pal/signal.rs +++ b/src/platform/pal/signal.rs @@ -42,6 +42,6 @@ pub trait PalSignal: Pal { fn sigtimedwait( set: &sigset_t, sig: Option<&mut siginfo_t>, - tp: ×pec, + tp: Option<×pec>, ) -> Result<(), Errno>; } diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs index a5f0c10509..0ae511bbec 100644 --- a/src/platform/redox/signal.rs +++ b/src/platform/redox/signal.rs @@ -274,14 +274,14 @@ impl PalSignal for Sys { fn sigtimedwait( set: &sigset_t, info_out: Option<&mut siginfo_t>, - timeout: ×pec, + timeout: Option<×pec>, ) -> Result<(), Errno> { // TODO: deadline-based API - let timeout = syscall::TimeSpec { + let timeout = timeout.map(|timeout| syscall::TimeSpec { tv_sec: timeout.tv_sec, tv_nsec: timeout.tv_nsec as _, - }; - let info = redox_rt::signal::await_signal_sync(*set, &timeout)?.into(); + }); + let info = redox_rt::signal::await_signal_sync(*set, timeout.as_ref())?.into(); if let Some(out) = info_out { *out = info; } diff --git a/tests/sigqueue.c b/tests/sigqueue.c index 78e4eeffd5..d4afe0a862 100644 --- a/tests/sigqueue.c +++ b/tests/sigqueue.c @@ -24,11 +24,11 @@ void validate(int sig, const siginfo_t *info) { } void action(int sig, siginfo_t *info, void *context) { - num++; (void)context; assert(context != NULL); validate(sig, info); write(1, "action\n", 7); + num++; } int main(void) { @@ -40,12 +40,6 @@ int main(void) { parent = getpid(); assert(parent != 0); - int child = fork(); - ERROR_IF(fork, child, == -1); - - status = close(fds[child == 0 ? 0 : 1]); - ERROR_IF(close, status, == -1); - sigset_t set, mask; status = sigfillset(&mask); ERROR_IF(sigfillset, status, == -1); @@ -67,6 +61,16 @@ int main(void) { status = sigaddset(&set, THE_SIG); ERROR_IF(sigaddset, status, == -1); + sigset_t empty_set; + status = sigemptyset(&empty_set); + ERROR_IF(sigemptyset, status, == -1); + + int child = fork(); + ERROR_IF(fork, child, == -1); + + status = close(fds[child == 0 ? 0 : 1]); + ERROR_IF(close, status, == -1); + struct sigaction sa; memcpy(&sa.sa_mask, &set, sizeof (sigset_t)); sa.sa_flags = SA_SIGINFO; @@ -84,6 +88,20 @@ int main(void) { validate(THE_SIG, &info); assert(num == 0); // ensure no signal handler ran + num++; + + // TODO: check status + status = sigsuspend(&empty_set); + if (status == -1) { + perror("error in sigsuspend"); + puts("[EINTR] is usually expected"); + } + + assert(num == 2); // ensure signal handler ran + + status = sigprocmask(SIG_SETMASK, &empty_set, NULL); + ERROR_IF(sigprocmask, status, == -1); + while (num < 31) {} status = write(fds[1], "A", 1); From af6435e12d96c6757719bf194810dcf2e92c0a2e Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 4 Aug 2024 23:25:29 +0200 Subject: [PATCH 24/27] Implement psignal and psiginfo. --- src/header/signal/mod.rs | 60 ++++++++++++++++++++++++++++++++++++++-- src/header/string/mod.rs | 4 +-- tests/Makefile | 1 + tests/psignal.c | 22 +++++++++++++++ 4 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 tests/psignal.c diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs index dbd6d69ced..a8785a8807 100644 --- a/src/header/signal/mod.rs +++ b/src/header/signal/mod.rs @@ -5,14 +5,15 @@ use core::{mem, ptr}; use cbitset::BitSet; use crate::{ - error::{self, Errno, ResultExt}, + error::{Errno, ResultExt}, + c_str::CStr, header::{errno, time::timespec}, platform::{self, types::*, Pal, PalSignal, Sys}, }; pub use self::sys::*; -use super::errno::EFAULT; +use super::{errno::EFAULT, unistd}; #[cfg(target_os = "linux")] #[path = "linux.rs"] @@ -54,6 +55,7 @@ pub struct sigaltstack { pub ss_size: size_t, } +// FIXME: This struct is wrong on Linux #[repr(C)] #[derive(Clone, Copy)] pub struct siginfo { @@ -395,7 +397,7 @@ pub unsafe extern "C" fn sigwaitinfo(set: *const sigset_t, sig: *mut siginfo_t) sigtimedwait(set, sig, core::ptr::null()) } -pub const _signal_strings: [&str; 32] = [ +pub(crate) const SIGNAL_STRINGS: [&str; 32] = [ "Unknown signal\0", "Hangup\0", "Interrupt\0", @@ -429,3 +431,55 @@ pub const _signal_strings: [&str; 32] = [ "Power failure\0", "Bad system call\0", ]; +#[no_mangle] +pub unsafe extern "C" fn psignal(sig: c_int, prefix: *const c_char) { + let c_description = usize::try_from(sig) + .ok() + .and_then(|idx| SIGNAL_STRINGS.get(idx)) + .unwrap_or(&SIGNAL_STRINGS[0]); + let description = &c_description[..c_description.len() - 1]; + let prefix = CStr::from_ptr(prefix).to_string_lossy(); + // TODO: stack vec or print directly? + let string = alloc::format!("{prefix}:{description}\n"); + // TODO: better internal libc API? + let _ = unistd::write( + unistd::STDERR_FILENO, + string.as_bytes().as_ptr().cast(), + string.as_bytes().len(), + ); +} +#[no_mangle] +pub unsafe extern "C" fn psiginfo(info: *const siginfo_t, prefix: *const c_char) { + let siginfo_t { + si_code, + si_signo, + si_pid, + si_uid, + si_errno, + si_addr, + si_status, + si_value, + } = &*info; + let sival_ptr = si_value.sival_ptr; + let prefix = CStr::from_ptr(prefix).to_string_lossy(); + // TODO: stack vec or print directly? + let string = alloc::format!( + "{prefix}:siginfo_t {{ + si_code: {si_code} + si_signo: {si_signo} + si_pid: {si_pid} + si_uid: {si_uid} + si_errno: {si_errno} + si_addr: {si_addr:p} + si_status: {si_status} + si_value: {sival_ptr:p} +}} +" + ); + // TODO: better internal libc API? + let _ = unistd::write( + unistd::STDERR_FILENO, + string.as_bytes().as_ptr().cast(), + string.as_bytes().len(), + ); +} diff --git a/src/header/string/mod.rs b/src/header/string/mod.rs index 0d16d6d2d1..9ddc6cb37a 100644 --- a/src/header/string/mod.rs +++ b/src/header/string/mod.rs @@ -353,9 +353,9 @@ pub unsafe extern "C" fn strrchr(s: *const c_char, c: c_int) -> *mut c_char { #[no_mangle] pub unsafe extern "C" fn strsignal(sig: c_int) -> *mut c_char { - signal::_signal_strings + signal::SIGNAL_STRINGS .get(sig as usize) - .unwrap_or(&signal::_signal_strings[0]) // Unknown signal message + .unwrap_or(&signal::SIGNAL_STRINGS[0]) // Unknown signal message .as_ptr() as *mut c_char } diff --git a/tests/Makefile b/tests/Makefile index b2ce84cbf7..0865ff9928 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -25,6 +25,7 @@ EXPECT_NAMES=\ locale \ math \ netdb/getaddrinfo \ + psignal \ ptrace \ regex \ select \ diff --git a/tests/psignal.c b/tests/psignal.c new file mode 100644 index 0000000000..375d1b0552 --- /dev/null +++ b/tests/psignal.c @@ -0,0 +1,22 @@ +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + puts("------psignal------"); + psignal(SIGUSR1, "a prefix"); + puts("------ end ------"); + puts("------psiginfo-----"); + siginfo_t info = { 0 }; + info.si_code = SI_USER; + info.si_pid = 42; + info.si_uid = 1337; + info.si_addr = (void *)0xdeadbeef; + info.si_value.sival_ptr = (void *)0xfedface; + psiginfo(&info, "another prefix"); + puts("------ -----"); +} From fed49a29af87bd4bf5e4ad652651ffee6d6d1c11 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 5 Aug 2024 13:22:18 +0200 Subject: [PATCH 25/27] Properly deallocate thread stack on pthread_exit. --- redox-rt/src/thread.rs | 20 ++++++++++++++++---- src/platform/linux/mod.rs | 2 +- src/platform/pal/mod.rs | 2 +- src/platform/redox/mod.rs | 4 ++-- src/pthread/mod.rs | 12 ++++++------ 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/redox-rt/src/thread.rs b/redox-rt/src/thread.rs index 664588ab77..1791732a22 100644 --- a/redox-rt/src/thread.rs +++ b/redox-rt/src/thread.rs @@ -1,6 +1,8 @@ +use core::mem::size_of; + use syscall::{Result, O_CLOEXEC}; -use crate::{arch::*, proc::*, RtTcb}; +use crate::{arch::*, proc::*, signal::tmp_disable_signals, 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 { @@ -48,10 +50,20 @@ pub unsafe fn rlct_clone_impl(stack: *mut usize) -> Result { Ok(new_thr_fd) } -pub fn exit_this_thread() -> ! { - let thread_fd = RtTcb::current().thread_fd(); +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(); + + 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(); - syscall::write(status_fd, &usize::MAX.to_ne_bytes()).unwrap(); + let mut buf = [0; size_of::() * 3]; + plain::slice_from_mut_bytes(&mut buf) + .unwrap() + .copy_from_slice(&[usize::MAX, stack_base as usize, stack_size]); + syscall::write(status_fd, &buf).unwrap(); unreachable!() } diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 0646f1b78c..b258b52da5 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -157,7 +157,7 @@ impl Pal for Sys { } loop {} } - fn exit_thread() -> ! { + unsafe fn exit_thread(_stack_base: *mut (), _stack_size: usize) -> ! { // TODO Self::exit(0) } diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index 3a234dfec3..09dea6cba9 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -55,7 +55,7 @@ pub trait Pal { fn exit(status: c_int) -> !; - fn exit_thread() -> !; + unsafe fn exit_thread(stack_base: *mut (), stack_size: usize) -> !; fn fchdir(fildes: c_int) -> c_int; diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index a3bda34315..57482b4d9b 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -1137,7 +1137,7 @@ impl Pal for Sys { (unsafe { syscall::syscall5(syscall::number::SYS_GETPID, !0, !0, !0, !0, !0) }).is_ok() } - fn exit_thread() -> ! { - redox_rt::thread::exit_this_thread() + unsafe fn exit_thread(stack_base: *mut (), stack_size: usize) -> ! { + redox_rt::thread::exit_this_thread(stack_base, stack_size) } } diff --git a/src/pthread/mod.rs b/src/pthread/mod.rs index 2d2fc7b20f..d7b97fe1de 100644 --- a/src/pthread/mod.rs +++ b/src/pthread/mod.rs @@ -2,7 +2,7 @@ use core::{ cell::{Cell, UnsafeCell}, - mem::MaybeUninit, + mem::{offset_of, MaybeUninit}, ptr::{addr_of, NonNull}, sync::atomic::{AtomicBool, AtomicUsize, Ordering}, }; @@ -22,8 +22,6 @@ use crate::{ use crate::sync::{waitval::Waitval, Mutex}; -const MAIN_PTHREAD_ID: usize = 1; - /// Called only by the main thread, as part of relibc_start. pub unsafe fn init() { Tcb::current() @@ -269,7 +267,7 @@ pub unsafe fn join(thread: &Pthread) -> Result { pub unsafe fn detach(thread: &Pthread) -> Result<(), Errno> { thread .flags - .fetch_or(PthreadFlags::DETACHED.bits(), Ordering::Release); + .fetch_or(PthreadFlags::DETACHED.bits(), Ordering::AcqRel); Ok(()) } @@ -294,6 +292,8 @@ pub unsafe fn exit_current_thread(retval: Retval) -> ! { header::tls::run_all_destructors(); let this = current_thread().expect("failed to obtain current thread when exiting"); + let stack_base = this.stack_base; + let stack_size = this.stack_size; if this.flags.load(Ordering::Acquire) & PthreadFlags::DETACHED.bits() != 0 { // When detached, the thread state no longer makes any sense, and can immediately be @@ -304,12 +304,12 @@ pub unsafe fn exit_current_thread(retval: Retval) -> ! { this.waitval.post(retval); } - Sys::exit_thread() + Sys::exit_thread(stack_base.cast(), stack_size) } unsafe fn dealloc_thread(thread: &Pthread) { + // TODO: How should this be handled on Linux? OS_TID_TO_PTHREAD.lock().remove(&thread.os_tid.get().read()); - //drop(Box::from_raw(thread as *const Pthread as *mut Pthread)); } pub const SIGRT_RLCT_CANCEL: usize = 33; pub const SIGRT_RLCT_TIMER: usize = 34; From 79e1550215fc7031e8f575e70e84cab7e34dea61 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 5 Aug 2024 16:09:36 +0200 Subject: [PATCH 26/27] Use only sig & 63 when checking SA_ONSTACK. --- redox-rt/src/arch/x86_64.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs index 56cebb8f8c..f1e5827ece 100644 --- a/redox-rt/src/arch/x86_64.rs +++ b/redox-rt/src/arch/x86_64.rs @@ -240,9 +240,12 @@ asmfunction!(__relibc_internal_sigentry: [" // skip the sigaltstack logic. lea rdx, [rip + {pctl} + {pctl_off_actions}] + mov ecx, eax + and ecx, 63 + // LEA doesn't support 16x, so just do two x8s. - lea rdx, [rdx + 8 * rax] - lea rdx, [rdx + 8 * rax] + lea rdx, [rdx + 8 * rcx] + lea rdx, [rdx + 8 * rcx] bt qword ptr [rdx], {SA_ONSTACK_BIT} jnc 4f From 293b041ea74bc9d7f8b417283cf55e2eb668c9bb Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 23 Sep 2024 22:50:11 +0200 Subject: [PATCH 27/27] Move psignal test to correct (non-expect) category. --- tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile b/tests/Makefile index 0865ff9928..73bb54dc20 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -25,7 +25,6 @@ EXPECT_NAMES=\ locale \ math \ netdb/getaddrinfo \ - psignal \ ptrace \ regex \ select \ @@ -160,6 +159,7 @@ NAMES=\ $(EXPECT_NAMES) \ dirent/main \ pty/forkpty \ + psignal \ pwd \ sa_restart \ sigchld \