From 901bccadfe6fc9a23e257fcaf65dfed5fa778888 Mon Sep 17 00:00:00 2001 From: anbo Date: Mon, 3 Jun 2024 15:03:15 +0800 Subject: [PATCH] Add syscall rt_sigpending --- docs/src/kernel/linux-compatibility.md | 2 +- .../aster-nix/src/process/posix_thread/mod.rs | 11 ++- .../aster-nix/src/process/signal/sig_mask.rs | 5 +- .../src/process/signal/sig_queues.rs | 31 ++++++- kernel/aster-nix/src/syscall/arch/x86.rs | 2 + kernel/aster-nix/src/syscall/mod.rs | 1 + kernel/aster-nix/src/syscall/rt_sigpending.rs | 30 +++++++ regression/apps/signal_c/signal_test.c | 90 +++++++++++++++++++ 8 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 kernel/aster-nix/src/syscall/rt_sigpending.rs diff --git a/docs/src/kernel/linux-compatibility.md b/docs/src/kernel/linux-compatibility.md index d751a1c2..465f5ff2 100644 --- a/docs/src/kernel/linux-compatibility.md +++ b/docs/src/kernel/linux-compatibility.md @@ -147,7 +147,7 @@ provided by Linux on x86-64 architecture. | 124 | getsid | ✅ | | 125 | capget | ❌ | | 126 | capset | ❌ | -| 127 | rt_sigpending | ❌ | +| 127 | rt_sigpending | ✅ | | 128 | rt_sigtimedwait | ❌ | | 129 | rt_sigqueueinfo | ❌ | | 130 | rt_sigsuspend | ✅ | diff --git a/kernel/aster-nix/src/process/posix_thread/mod.rs b/kernel/aster-nix/src/process/posix_thread/mod.rs index 2a529223..d49fe0ee 100644 --- a/kernel/aster-nix/src/process/posix_thread/mod.rs +++ b/kernel/aster-nix/src/process/posix_thread/mod.rs @@ -8,8 +8,11 @@ use super::{ do_exit_group, kill::SignalSenderIds, signal::{ - sig_mask::SigMask, sig_num::SigNum, sig_queues::SigQueues, signals::Signal, SigEvents, - SigEventsFilter, SigStack, + sig_mask::{SigMask, SigSet}, + sig_num::SigNum, + sig_queues::SigQueues, + signals::Signal, + SigEvents, SigEventsFilter, SigStack, }, Credentials, Process, TermStatus, }; @@ -82,6 +85,10 @@ impl PosixThread { &self.sig_mask } + pub fn sig_pending(&self) -> SigSet { + self.sig_queues.sig_pending() + } + pub fn has_pending_signal(&self) -> bool { !self.sig_queues.is_empty() } diff --git a/kernel/aster-nix/src/process/signal/sig_mask.rs b/kernel/aster-nix/src/process/signal/sig_mask.rs index 12440c7e..b82ebc0f 100644 --- a/kernel/aster-nix/src/process/signal/sig_mask.rs +++ b/kernel/aster-nix/src/process/signal/sig_mask.rs @@ -3,9 +3,12 @@ use super::{constants::MIN_STD_SIG_NUM, sig_num::SigNum}; use crate::prelude::*; +/// A signal mask. +pub type SigMask = SigSet; + #[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Pod)] #[repr(C)] -pub struct SigMask { +pub struct SigSet { bits: u64, } diff --git a/kernel/aster-nix/src/process/signal/sig_queues.rs b/kernel/aster-nix/src/process/signal/sig_queues.rs index 903ae14f..46d4deb4 100644 --- a/kernel/aster-nix/src/process/signal/sig_queues.rs +++ b/kernel/aster-nix/src/process/signal/sig_queues.rs @@ -3,7 +3,11 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use super::{ - constants::*, sig_mask::SigMask, sig_num::SigNum, signals::Signal, SigEvents, SigEventsFilter, + constants::*, + sig_mask::{SigMask, SigSet}, + sig_num::SigNum, + signals::Signal, + SigEvents, SigEventsFilter, }; use crate::{ events::{Observer, Subject}, @@ -57,6 +61,11 @@ impl SigQueues { signal } + pub fn sig_pending(&self) -> SigSet { + let queues = self.queues.lock(); + queues.sig_pending() + } + pub fn register_observer( &self, observer: Weak>, @@ -191,4 +200,24 @@ impl Queues { let idx = (signum.as_u8() - MIN_RT_SIG_NUM) as usize; &mut self.rt_queues[idx] } + + fn sig_pending(&self) -> SigSet { + let mut pending = SigSet::new_empty(); + + // Process standard signal queues + for (idx, signal) in self.std_queues.iter().enumerate() { + if signal.is_some() { + pending.add_signal(SigNum::from_u8(idx as u8 + MIN_STD_SIG_NUM)); + } + } + + // Process real-time signal queues + for (idx, signals) in self.rt_queues.iter().enumerate() { + if !signals.is_empty() { + pending.add_signal(SigNum::from_u8(idx as u8 + MIN_RT_SIG_NUM)); + } + } + + pending + } } diff --git a/kernel/aster-nix/src/syscall/arch/x86.rs b/kernel/aster-nix/src/syscall/arch/x86.rs index d04eee9d..b1151335 100644 --- a/kernel/aster-nix/src/syscall/arch/x86.rs +++ b/kernel/aster-nix/src/syscall/arch/x86.rs @@ -69,6 +69,7 @@ use crate::syscall::{ rename::{sys_rename, sys_renameat}, rmdir::sys_rmdir, rt_sigaction::sys_rt_sigaction, + rt_sigpending::sys_rt_sigpending, rt_sigprocmask::sys_rt_sigprocmask, rt_sigreturn::sys_rt_sigreturn, rt_sigsuspend::sys_rt_sigsuspend, @@ -208,6 +209,7 @@ impl_syscall_nums_and_dispatch_fn! { SYS_SETFSUID = 122 => sys_setfsuid(args[..1]); SYS_SETFSGID = 123 => sys_setfsgid(args[..1]); SYS_GETSID = 124 => sys_getsid(args[..1]); + SYS_RT_SIGPENDING = 127 => sys_rt_sigpending(args[..2]); SYS_RT_SIGSUSPEND = 130 => sys_rt_sigsuspend(args[..2]); SYS_SIGALTSTACK = 131 => sys_sigaltstack(args[..2]); SYS_STATFS = 137 => sys_statfs(args[..2]); diff --git a/kernel/aster-nix/src/syscall/mod.rs b/kernel/aster-nix/src/syscall/mod.rs index d81da552..33888cd0 100644 --- a/kernel/aster-nix/src/syscall/mod.rs +++ b/kernel/aster-nix/src/syscall/mod.rs @@ -76,6 +76,7 @@ mod recvfrom; mod rename; mod rmdir; mod rt_sigaction; +mod rt_sigpending; mod rt_sigprocmask; mod rt_sigreturn; mod rt_sigsuspend; diff --git a/kernel/aster-nix/src/syscall/rt_sigpending.rs b/kernel/aster-nix/src/syscall/rt_sigpending.rs new file mode 100644 index 00000000..c4af29bb --- /dev/null +++ b/kernel/aster-nix/src/syscall/rt_sigpending.rs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MPL-2.0 + +use super::SyscallReturn; +use crate::{prelude::*, process::posix_thread::PosixThreadExt, util::write_val_to_user}; + +pub fn sys_rt_sigpending(u_set_ptr: Vaddr, sigset_size: usize) -> Result { + debug!( + "u_set_ptr = 0x{:x}, sigset_size = {}", + u_set_ptr, sigset_size + ); + if sigset_size != 8 { + return_errno_with_message!(Errno::EINVAL, "sigset size is not equal to 8") + } + do_rt_sigpending(u_set_ptr, sigset_size)?; + Ok(SyscallReturn::Return(0)) +} + +fn do_rt_sigpending(set_ptr: Vaddr, sigset_size: usize) -> Result<()> { + let current_thread = current_thread!(); + let posix_thread = current_thread.as_posix_thread().unwrap(); + + let combined_signals = { + let sig_mask_value = posix_thread.sig_mask().lock().as_u64(); + let sig_pending_value = posix_thread.sig_pending().as_u64(); + sig_mask_value & sig_pending_value + }; + + write_val_to_user(set_ptr, &combined_signals)?; + Ok(()) +} diff --git a/regression/apps/signal_c/signal_test.c b/regression/apps/signal_c/signal_test.c index c529ba45..7adedf5d 100644 --- a/regression/apps/signal_c/signal_test.c +++ b/regression/apps/signal_c/signal_test.c @@ -364,6 +364,95 @@ int test_sigaltstack() return 0; } +// ============================================================================ +// Test sigpending +// ============================================================================ +int test_sigpending() +{ + int ret; + + // Set up signal handler for SIGSEGV and SIGIO + struct sigaction new_action, old_sigsegv_action, old_sigio_action; + memset(&new_action, 0, sizeof(struct sigaction)); + memset(&old_sigsegv_action, 0, sizeof(struct sigaction)); + new_action.sa_sigaction = handle_sigsegv; + new_action.sa_flags = SA_SIGINFO; + if (sigaction(SIGSEGV, &new_action, &old_sigsegv_action) < 0) { + THROW_ERROR("registering new signal handler failed"); + } + if (old_sigsegv_action.sa_handler != SIG_DFL) { + THROW_ERROR("unexpected old sig handler"); + } + + memset(&new_action, 0, sizeof(struct sigaction)); + memset(&old_sigio_action, 0, sizeof(struct sigaction)); + new_action.sa_sigaction = handle_sigio; + new_action.sa_flags = SA_SIGINFO | SA_NODEFER; + if (sigaction(SIGIO, &new_action, &old_sigio_action) < 0) { + THROW_ERROR("registering new signal handler failed"); + } + if (old_sigio_action.sa_handler != SIG_DFL) { + THROW_ERROR("unexpected old sig handler"); + } + + // Block SIGSEGV and SIGIO + sigset_t new_set, old_set, pending_set; + sigfillset(&new_set); + sigaddset(&new_set, SIGSEGV); + sigaddset(&new_set, SIGIO); + if ((ret = sigprocmask(SIG_BLOCK, &new_set, &old_set)) < 0) { + THROW_ERROR("sigprocmask failed unexpectedly"); + } + + // Send SIGSEGV and SIGIO signals to the current process twice + kill(getpid(), SIGSEGV); + kill(getpid(), SIGSEGV); // Repeat + kill(getpid(), SIGIO); + kill(getpid(), SIGIO); // Repeat + + // Check for pending signals + if (sigpending(&pending_set) < 0) { + THROW_ERROR("sigpending failed unexpectedly"); + } + + if (!sigismember(&pending_set, SIGSEGV)) { + THROW_ERROR("SIGSEGV is not pending"); + } + + if (!sigismember(&pending_set, SIGIO)) { + THROW_ERROR("SIGIO (real-time signal) is not pending"); + } + + // Unblock all signals and check if pending signals are cleared + if (sigprocmask(SIG_SETMASK, &old_set, NULL) < 0) { + THROW_ERROR( + "sigprocmask failed unexpectedly, failed to restore signal mask"); + } + + // Fetch and check pending signals after unblocking + if (sigpending(&pending_set) < 0) { + THROW_ERROR("sigpending failed unexpectedly"); + } + + if (sigismember(&pending_set, SIGSEGV)) { + THROW_ERROR("SIGSEGV is pending"); + } + + if (sigismember(&pending_set, SIGIO)) { + THROW_ERROR("SIGIO (real-time signal) is pending"); + } + + // Restore old sigaction + if (sigaction(SIGSEGV, &old_sigsegv_action, NULL) < 0) { + THROW_ERROR("restoring old signal handler failed"); + } + if (sigaction(SIGIO, &old_sigio_action, NULL) < 0) { + THROW_ERROR("restoring old signal handler failed"); + } + + return 0; +} + int main() { test_sigprocmask(); @@ -372,5 +461,6 @@ int main() test_handle_sigsegv(); test_sigchld(); test_sigaltstack(); + test_sigpending(); return 0; }