/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ // signal handling related tests. use nix::sys::signal::Signal; use reverie::syscalls::AddrMut; use reverie::syscalls::ExitGroup; use reverie::syscalls::MemoryAccess; use reverie::syscalls::RtSigpending; use reverie::syscalls::Syscall; use reverie::syscalls::SyscallInfo; use reverie::syscalls::Sysno; use reverie::Error; use reverie::Guest; use reverie::Tool; #[derive(Debug, Default, Clone)] struct LocalState; #[reverie::tool] impl Tool for LocalState { async fn handle_syscall_event>( &self, guest: &mut T, syscall: Syscall, ) -> Result { if syscall.number() == Sysno::exit_group { let sigset_rptr = 0x7000_0100usize; let sigset: AddrMut = AddrMut::from_raw(sigset_rptr as _).unwrap(); let exit_failure = ExitGroup::new().with_status(1); let exit_success = syscall; if guest .inject( RtSigpending::new() .with_set(Some(sigset)) .with_sigsetsize(8usize), ) .await .is_ok() { let memory = guest.memory(); let pending: u64 = memory.read_value(sigset.cast())?; if pending != 1u64 << (Signal::SIGVTALRM as i32 - 1) { guest.tail_inject(exit_failure).await } else { guest.tail_inject(exit_success).await } } else { guest.tail_inject(exit_success).await } } else { guest.tail_inject(syscall).await } } } #[cfg(all(not(sanitized), test))] mod tests { use std::io; use std::mem::MaybeUninit; use nix::sys::signal; use reverie_ptrace::testing::check_fn; use super::*; // kernel_sigset_t used by naked syscall #[derive(Clone, Copy, PartialEq, Eq, Debug)] struct KernelSigset(u64); impl From<&[Signal]> for KernelSigset { fn from(signals: &[Signal]) -> Self { let mut set: u64 = 0; for &sig in signals { set |= 1u64 << (sig as usize - 1); } KernelSigset(set) } } #[allow(dead_code)] unsafe fn block_signals(signals: &[Signal]) -> io::Result { let set = KernelSigset::from(signals); let mut oldset: MaybeUninit = MaybeUninit::uninit(); if libc::syscall( libc::SYS_rt_sigprocmask, libc::SIG_BLOCK, &set as *const _, oldset.as_mut_ptr(), 8, ) != 0 { Err(io::Error::last_os_error()) } else { Ok(KernelSigset(oldset.assume_init())) } } #[test] // The actual test is in `handle_syscall_event`. To test we can get // pending signals from tracee, by injecting rt_sigpending. fn can_get_pending_signals() { check_fn::(|| { assert!(unsafe { block_signals(&[Signal::SIGVTALRM]) }.is_ok()); assert!(signal::raise(Signal::SIGVTALRM).is_ok()); unsafe { libc::syscall(libc::SYS_exit_group, 0) }; }); } }