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] 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);