From e96a1348e5f99d025de6c9e4494768ff4d8c731f Mon Sep 17 00:00:00 2001 From: ClawSeven Date: Fri, 22 Dec 2023 12:00:14 +0800 Subject: [PATCH] [test] Implement ut for sigsuspend --- demos/linux-ltp/syscalls-occlum | 3 -- src/libos/src/interrupt/mod.rs | 24 +++++++--- src/libos/src/net/syscalls.rs | 35 +++++++------- src/libos/src/signal/do_sigpending.rs | 1 + src/libos/src/signal/do_sigprocmask.rs | 21 +++----- src/libos/src/signal/do_sigsuspend.rs | 46 +++++++++++------- src/libos/src/signal/do_sigtimedwait.rs | 21 +++----- src/libos/src/signal/mod.rs | 2 +- src/libos/src/signal/sig_queues.rs | 35 -------------- src/libos/src/signal/syscalls.rs | 22 +++++---- src/libos/src/syscall/mod.rs | 4 +- test/Makefile | 4 +- test/pselect/main.c | 19 ++++---- test/sigsuspend/Makefile | 5 ++ test/sigsuspend/main.c | 64 +++++++++++++++++++++++++ 15 files changed, 174 insertions(+), 132 deletions(-) create mode 100644 test/sigsuspend/Makefile create mode 100644 test/sigsuspend/main.c diff --git a/demos/linux-ltp/syscalls-occlum b/demos/linux-ltp/syscalls-occlum index 0ea09dd3..3b41456b 100644 --- a/demos/linux-ltp/syscalls-occlum +++ b/demos/linux-ltp/syscalls-occlum @@ -1152,7 +1152,6 @@ rt_sigaction03 rt_sigaction03 rt_sigprocmask01 rt_sigprocmask01 #rt_sigprocmask02 rt_sigprocmask02 rt_sigqueueinfo01 rt_sigqueueinfo01 -rt_sigsuspend01 rt_sigsuspend01 rt_sigtimedwait01 rt_sigtimedwait01 rt_tgsigqueueinfo01 rt_tgsigqueueinfo01 @@ -1448,8 +1447,6 @@ sigprocmask01 sigprocmask01 sigrelse01 sigrelse01 -sigsuspend01 sigsuspend01 - sigtimedwait01 sigtimedwait01 sigwait01 sigwait01 diff --git a/src/libos/src/interrupt/mod.rs b/src/libos/src/interrupt/mod.rs index 1c45a8f4..df47ed97 100644 --- a/src/libos/src/interrupt/mod.rs +++ b/src/libos/src/interrupt/mod.rs @@ -38,15 +38,25 @@ pub fn do_handle_interrupt( /// Broadcast interrupts to threads by sending POSIX signals. pub fn broadcast_interrupts() -> Result { let should_interrupt_thread = |thread: &&ThreadRef| -> bool { - let should_interrupt = thread.process().is_forced_to_exit() - || thread.is_forced_to_stop() - || !thread.process().sig_queues().read().unwrap().empty(); + if thread.process().is_forced_to_exit() || thread.is_forced_to_stop() { + return true; + } - // Check Thread::sig_mask to avoid wrong interrupts - let sig_queues = thread.sig_queues().read().unwrap(); - let sig_mask = thread.sig_mask().read().unwrap(); + let sig_mask_guard = thread.sig_mask().read().unwrap(); + let interested = !*sig_mask_guard; + drop(sig_mask_guard); - should_interrupt || sig_queues.has_valid_signal(&sig_mask) + let thread_pending_sig = thread.sig_queues().read().unwrap().pending() & interested; + if !thread_pending_sig.empty() { + return true; + } + + let process_pending_sig = + thread.process().sig_queues().read().unwrap().pending() & interested; + if !process_pending_sig.empty() { + return true; + } + false }; let num_signaled_threads = crate::process::table::get_all_threads() diff --git a/src/libos/src/net/syscalls.rs b/src/libos/src/net/syscalls.rs index d78cef66..dad9f2cc 100644 --- a/src/libos/src/net/syscalls.rs +++ b/src/libos/src/net/syscalls.rs @@ -7,7 +7,7 @@ use super::io_multiplexing::{AsEpollFile, EpollCtl, EpollFile, EpollFlags, FdSet use fs::{CreationFlags, File, FileDesc, FileRef}; use misc::resource_t; use process::Process; -use signal::{do_sigprocmask, sigset_t, MaskOp, SigSet}; +use signal::{sigset_t, MaskOp, SigSet, SIGKILL, SIGSTOP}; use std::convert::TryFrom; use time::{timespec_t, timeval_t}; use util::mem_util::from_user; @@ -723,9 +723,9 @@ pub fn do_select( ret } -#[derive(Clone, Copy, Debug)] +#[derive(Debug)] #[repr(C)] -pub struct Pselect6sig { +pub struct sigset_argpack { ss: *const sigset_t, ss_len: size_t, } @@ -736,11 +736,13 @@ pub fn do_pselect6( writefds: *mut libc::fd_set, exceptfds: *mut libc::fd_set, timeout: *mut timespec_t, - sig_data: *const Pselect6sig, + sig_data: *const sigset_argpack, ) -> Result { let mut is_set_sig = false; - let mut original_sig_set = sigset_t::default(); + let mut prev_mask = SigSet::default(); + let thread = current!(); + // Set signal mask if !sig_data.is_null() { from_user::check_ptr(sig_data)?; let user_sig_data = unsafe { &*(sig_data) }; @@ -750,15 +752,20 @@ pub fn do_pselect6( if user_sig_data.ss_len != std::mem::size_of::() { return_errno!(EINVAL, "unexpected sigset size"); } - let op_and_set = { - let op = MaskOp::SetMask; + let update_mask = { let sigset = user_sig_data.ss; from_user::check_ptr(sigset)?; let set = unsafe { &*sigset }; - Some((op, set)) + let mut set = SigSet::from_c(unsafe { *sigset }); + // According to man pages, "it is not possible to block SIGKILL or SIGSTOP. + // Attempts to do so are silently ignored." + set -= SIGKILL; + set -= SIGSTOP; + set }; - - do_sigprocmask::do_rt_sigprocmask(op_and_set, Some(&mut original_sig_set))?; + let mut curr_mask = thread.sig_mask().write().unwrap(); + prev_mask = *curr_mask; + *curr_mask = update_mask; } } @@ -815,12 +822,8 @@ pub fn do_pselect6( // Restore the original signal mask if is_set_sig { - let op_and_set = { - let op = MaskOp::SetMask; - let set = &original_sig_set; - Some((op, set)) - }; - do_sigprocmask::do_rt_sigprocmask(op_and_set, None)?; + let mut curr_mask = thread.sig_mask().write().unwrap(); + *curr_mask = prev_mask; } ret diff --git a/src/libos/src/signal/do_sigpending.rs b/src/libos/src/signal/do_sigpending.rs index 843935f5..a1050ac6 100644 --- a/src/libos/src/signal/do_sigpending.rs +++ b/src/libos/src/signal/do_sigpending.rs @@ -6,6 +6,7 @@ pub fn do_sigpending() -> Result { let thread = current!(); let process = thread.process(); + let pending = (thread.sig_queues().read().unwrap().pending() | process.sig_queues().read().unwrap().pending()) & *thread.sig_mask().read().unwrap(); diff --git a/src/libos/src/signal/do_sigprocmask.rs b/src/libos/src/signal/do_sigprocmask.rs index de50e7c0..72c0b93e 100644 --- a/src/libos/src/signal/do_sigprocmask.rs +++ b/src/libos/src/signal/do_sigprocmask.rs @@ -2,24 +2,15 @@ use super::constants::*; use super::{sigset_t, SigSet}; use crate::prelude::*; -pub fn do_rt_sigprocmask( - op_and_set: Option<(MaskOp, &sigset_t)>, - oldset: Option<&mut sigset_t>, -) -> Result<()> { - debug!( - "do_rt_sigprocmask: op_and_set: {:?}, oldset: {:?}", - op_and_set.map(|(op, set)| (op, SigSet::from_c(*set))), - oldset - ); +pub fn do_rt_sigprocmask(op_and_set: Option<(MaskOp, &SigSet)>) -> Result { + debug!("do_rt_sigprocmask: op_and_set: {:?}", op_and_set,); let thread = current!(); let mut sig_mask = thread.sig_mask().write().unwrap(); - if let Some(oldset) = oldset { - *oldset = sig_mask.to_c(); - } - if let Some((op, &set)) = op_and_set { + let old_mask = *sig_mask; + if let Some((op, set)) = op_and_set { let set = { - let mut set = SigSet::from_c(set); + let mut set = *set; // According to man pages, "it is not possible to block SIGKILL or SIGSTOP. // Attempts to do so are silently ignored." set -= SIGKILL; @@ -38,7 +29,7 @@ pub fn do_rt_sigprocmask( } }; } - Ok(()) + Ok(old_mask) } #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/src/libos/src/signal/do_sigsuspend.rs b/src/libos/src/signal/do_sigsuspend.rs index fc8cd3c1..57180210 100644 --- a/src/libos/src/signal/do_sigsuspend.rs +++ b/src/libos/src/signal/do_sigsuspend.rs @@ -1,36 +1,46 @@ -use super::do_sigprocmask::do_rt_sigprocmask; +use super::constants::*; use super::do_sigtimedwait::PendingSigWaiter; use super::{sigset_t, MaskOp, SigNum, SigSet, Signal}; use crate::prelude::*; -pub fn do_sigsuspend(mask: &sigset_t) -> Result<()> { +pub fn do_sigsuspend(mask: &SigSet) -> Result<()> { debug!("do_sigsuspend: mask: {:?}", mask); let thread = current!(); let process = thread.process().clone(); - let mut original_sig_set = sigset_t::default(); // Set signal mask - let op_and_set = Some((MaskOp::SetMask, mask)); - do_rt_sigprocmask(op_and_set, Some(&mut original_sig_set))?; + let update_mask = { + let mut set = *mask; + // According to man pages, "it is not possible to block SIGKILL or SIGSTOP. + // Attempts to do so are silently ignored." + set -= SIGKILL; + set -= SIGSTOP; + set + }; - let interest = SigSet::from_c(!*mask); - let pending_sig_waiter = PendingSigWaiter::new(thread, process, interest); + let mut curr_mask = thread.sig_mask().write().unwrap(); + let prev_mask = *curr_mask; + *curr_mask = update_mask; + drop(curr_mask); - match pending_sig_waiter.suspend() { + // Suspend for interest signal + let interest = !update_mask; + let pending_sig_waiter = PendingSigWaiter::new(thread.clone(), process, interest); + + let err = match pending_sig_waiter.suspend() { Ok(_) => { - // Restore the original signal mask - let op_and_set = { - let op = MaskOp::SetMask; - let set = &original_sig_set; - Some((op, set)) - }; - do_rt_sigprocmask(op_and_set, None).unwrap(); - Err(errno!(EINTR, "Wait for EINTR signal successfully")) + errno!(EINTR, "Wait for EINTR signal successfully") } Err(_) => { // Impossible path - Err(errno!(EFAULT, "No interesting, pending signal")) + errno!(EFAULT, "No interesting, pending signal") } - } + }; + + // Restore the original signal mask + let mut curr_mask = thread.sig_mask().write().unwrap(); + *curr_mask = prev_mask; + + Err(err) } diff --git a/src/libos/src/signal/do_sigtimedwait.rs b/src/libos/src/signal/do_sigtimedwait.rs index c2dda831..28c6f97f 100644 --- a/src/libos/src/signal/do_sigtimedwait.rs +++ b/src/libos/src/signal/do_sigtimedwait.rs @@ -136,10 +136,9 @@ impl PendingSigWaiter { if has_interest_signal(&self.interest, &self.thread, &self.process) { return Ok(()); } - } else { - // Impossible case - return Err(e); } + // Impossible case + return Err(e); } } } @@ -164,17 +163,11 @@ impl Drop for PendingSigWaiter { } fn has_interest_signal(interest: &SigSet, thread: &ThreadRef, process: &ProcessRef) -> bool { - let blocked = !*interest; - process - .sig_queues() - .write() - .unwrap() - .has_valid_signal(&blocked) - || thread - .sig_queues() - .write() - .unwrap() - .has_valid_signal(&blocked) + let pending = (thread.sig_queues().read().unwrap().pending() + | process.sig_queues().read().unwrap().pending()) + & *interest; + + !pending.empty() } fn dequeue_pending_signal( diff --git a/src/libos/src/signal/mod.rs b/src/libos/src/signal/mod.rs index 13b84124..81574e87 100644 --- a/src/libos/src/signal/mod.rs +++ b/src/libos/src/signal/mod.rs @@ -22,6 +22,7 @@ mod do_kill; mod do_sigaction; mod do_sigaltstack; mod do_sigpending; +mod do_sigprocmask; mod do_sigreturn; mod do_sigsuspend; mod do_sigtimedwait; @@ -35,4 +36,3 @@ mod signals; mod syscalls; pub mod constants; -pub mod do_sigprocmask; diff --git a/src/libos/src/signal/sig_queues.rs b/src/libos/src/signal/sig_queues.rs index 0dccacb7..61926e56 100644 --- a/src/libos/src/signal/sig_queues.rs +++ b/src/libos/src/signal/sig_queues.rs @@ -38,41 +38,6 @@ impl SigQueues { self.count == 0 } - pub fn has_valid_signal(&self, blocked: &SigSet) -> bool { - // Fast path for the common case of no pending signals - if self.empty() { - return false; - } - - // Check standard signals. - for &signum in &Self::ORDERED_STD_SIGS { - if blocked.contains(signum) { - continue; - } - - let queue = self.get_std_queue(signum); - if queue.is_some() { - return true; - } - } - - // If no standard signals, then check real-time signals. - for signum in MIN_RT_SIG_NUM..=MAX_RT_SIG_NUM { - let signum = unsafe { SigNum::from_u8_unchecked(signum) }; - if blocked.contains(signum) { - continue; - } - - let queue = self.get_rt_queue(signum); - if !queue.is_empty() { - return true; - } - } - - // There must be pending but blocked signals - false - } - pub fn enqueue(&mut self, signal: Box) { let signum = signal.num(); if signum.is_std() { diff --git a/src/libos/src/signal/syscalls.rs b/src/libos/src/signal/syscalls.rs index 411a9177..76672b6f 100644 --- a/src/libos/src/signal/syscalls.rs +++ b/src/libos/src/signal/syscalls.rs @@ -89,23 +89,25 @@ pub fn do_rt_sigprocmask( if sigset_size != std::mem::size_of::() { return_errno!(EINVAL, "unexpected sigset size"); } + + let mut set_sig = SigSet::default(); let op_and_set = { if !set_ptr.is_null() { let op = MaskOp::from_u32(how as u32)?; - let set = unsafe { &*set_ptr }; - Some((op, set)) + set_sig = SigSet::from_c(unsafe { *set_ptr }); + Some((op, &set_sig)) } else { None } }; - let old_set = { - if !oldset_ptr.is_null() { - Some(unsafe { &mut *oldset_ptr }) - } else { - None + + let old_set = super::do_sigprocmask::do_rt_sigprocmask(op_and_set)?; + if !oldset_ptr.is_null() { + unsafe { + *oldset_ptr = old_set.to_c(); } - }; - super::do_sigprocmask::do_rt_sigprocmask(op_and_set, old_set)?; + } + Ok(0) } @@ -201,7 +203,7 @@ pub fn do_rt_sigsuspend(mask_ptr: *const sigset_t) -> Result { return_errno!(EFAULT, "ptr must not be null"); } from_user::check_ptr(mask_ptr)?; - unsafe { *mask_ptr } + SigSet::from_c(unsafe { *mask_ptr }) }; super::do_sigsuspend::do_sigsuspend(&mask)?; diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 6b17c0aa..36f1dc4a 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -45,7 +45,7 @@ use crate::net::{ do_epoll_pwait, do_epoll_wait, do_getpeername, do_getsockname, do_getsockopt, do_listen, do_poll, do_ppoll, do_pselect6, do_recvfrom, do_recvmsg, do_select, do_sendmmsg, do_sendmsg, do_sendto, do_setsockopt, do_shutdown, do_socket, do_socketpair, mmsghdr, msghdr, msghdr_mut, - Pselect6sig, + sigset_argpack, }; use crate::process::{ do_arch_prctl, do_clone, do_execve, do_exit, do_exit_group, do_futex, do_get_robust_list, @@ -367,7 +367,7 @@ macro_rules! process_syscall_table_with_callback { (Readlinkat = 267) => do_readlinkat(dirfd: i32, path: *const i8, buf: *mut u8, size: usize), (Fchmodat = 268) => do_fchmodat(dirfd: i32, path: *const i8, mode: u16), (Faccessat = 269) => do_faccessat(dirfd: i32, path: *const i8, mode: u32, flags: u32), - (Pselect6 = 270) => do_pselect6(nfds: c_int, readfds: *mut libc::fd_set, writefds: *mut libc::fd_set, exceptfds: *mut libc::fd_set, timeout: *mut timespec_t, data: *const Pselect6sig), + (Pselect6 = 270) => do_pselect6(nfds: c_int, readfds: *mut libc::fd_set, writefds: *mut libc::fd_set, exceptfds: *mut libc::fd_set, timeout: *mut timespec_t, data: *const sigset_argpack), (Ppoll = 271) => do_ppoll(fds: *mut libc::pollfd, nfds: libc::nfds_t, timeout_ts: *const timespec_t, sigmask: *const sigset_t), (Unshare = 272) => handle_unsupported(), (SetRobustList = 273) => do_set_robust_list(list_head_ptr: *mut RobustListHead, len: usize), diff --git a/test/Makefile b/test/Makefile index 20a5a9b1..b9c87c66 100644 --- a/test/Makefile +++ b/test/Makefile @@ -21,8 +21,8 @@ TEST_DEPS := client data_sink naughty_child TESTS ?= env empty hello_world malloc mmap file fs_perms getpid spawn sched pipe time timerfd \ truncate readdir mkdir open stat link symlink chmod chown tls pthread system_info rlimit \ server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group posix_flock \ - ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs wait pselect \ - spawn_attribute exec statfs random umask pgrp vfork mount flock utimes shm epoll brk posix_shm + ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs pselect sigsuspend \ + wait spawn_attribute exec statfs random umask pgrp vfork mount flock utimes shm epoll brk posix_shm # Benchmarks: need to be compiled and run by bench-% target BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput diff --git a/test/pselect/main.c b/test/pselect/main.c index bc36cac2..eebe13fd 100644 --- a/test/pselect/main.c +++ b/test/pselect/main.c @@ -6,6 +6,7 @@ #include #include // for uint64_t #include +#include "test.h" // Signal handler for SIGUSR1 void sigusr1_handler(int sig) { @@ -36,13 +37,13 @@ int main() { // Spawn new thread for sending signal when call pselect syscall pthread_t signal_thread; if (pthread_create(&signal_thread, NULL, send_signal, &main_thread_id) != 0) { - perror("pthread_create"); + THROW_ERROR("pthread_create"); return 1; } int timer_fd = timerfd_create(CLOCK_REALTIME, 0); if (timer_fd == -1) { - perror("timerfd_create"); + THROW_ERROR("timerfd_create"); return 1; } @@ -52,7 +53,7 @@ int main() { timerValue.it_interval.tv_sec = 0; timerValue.it_interval.tv_nsec = 0; if (timerfd_settime(timer_fd, 0, &timerValue, NULL) == -1) { - perror("timerfd_settime"); + THROW_ERROR("timerfd_settime"); close(timer_fd); return 1; } @@ -63,17 +64,17 @@ int main() { int ready = pselect(timer_fd + 1, &readfds, NULL, NULL, NULL, &sigmask); - if (ready == -1) { - perror("pselect6"); - } else if (ready == 0) { - // Impossible case - printf("No input - timeout reached\n"); - } else { + if (ready > 0) { if (FD_ISSET(timer_fd, &readfds)) { printf("Timer expired, pselect blocked SIGUSR1 signal successfully\n"); uint64_t expirations; read(timer_fd, &expirations, sizeof(expirations)); } + } else if (ready == 0) { + // Impossible case + printf("No input - timeout reached\n"); + } else { + THROW_ERROR("failed to pselect"); } pthread_join(signal_thread, NULL); diff --git a/test/sigsuspend/Makefile b/test/sigsuspend/Makefile new file mode 100644 index 00000000..5c1ee8c1 --- /dev/null +++ b/test/sigsuspend/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := -lpthread +BIN_ARGS := diff --git a/test/sigsuspend/main.c b/test/sigsuspend/main.c new file mode 100644 index 00000000..6679d01a --- /dev/null +++ b/test/sigsuspend/main.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include +#include // for uint64_t +#include +#include "test.h" + +// Signal handler for SIGUSR1 +void sigusr_handler(int sig) { + printf("Received signals: %d. ", sig); +} + +void *send_signal(void *arg) { + pthread_t main_thread_id = *(pthread_t *)arg; + sleep(1); + pthread_kill(main_thread_id, SIGUSR1); + sleep(1); + pthread_kill(main_thread_id, SIGUSR2); + return NULL; +} + +int main() { + // Set SIGUSR1 signal action + struct sigaction sa1; + sa1.sa_handler = sigusr_handler; + sigemptyset(&sa1.sa_mask); + sa1.sa_flags = 0; + sigaction(SIGUSR1, &sa1, NULL); + + // Set SIGUSR2 signal action + struct sigaction sa2; + sa2.sa_handler = sigusr_handler; + sigemptyset(&sa2.sa_mask); + sa2.sa_flags = 0; + sigaction(SIGUSR2, &sa2, NULL); + + // Mask for blocking SIGUSR1 signal + sigset_t sigmask; + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGUSR1); + + // Access pthread id + pthread_t main_thread_id = pthread_self(); + + // Spawn new thread for sending signal when call pselect syscall + pthread_t signal_thread; + if (pthread_create(&signal_thread, NULL, send_signal, &main_thread_id) != 0) { + THROW_ERROR("failed to create pthread"); + return 1; + } + + int ret = sigsuspend(&sigmask); + if (ret == -1) { + printf("Signal received, the rt_sigsuspend syscall returns successfully\n"); + } else { + THROW_ERROR("failed to call rt_sigsuspend syscall"); + } + + pthread_join(signal_thread, NULL); + return 0; +}