Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use linux_raw_sys::{

use crate::SignalSet;

#[derive(Debug)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DefaultSignalAction {
/// Terminate the process.
Terminate,
Expand All @@ -32,7 +32,7 @@ pub enum DefaultSignalAction {
/// Signal action that should be properly handled by the OS.
///
/// See [`SignalManager::check_signals`] for details.
#[derive(Debug)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SignalOSAction {
/// Terminate the process.
Terminate,
Expand Down
235 changes: 88 additions & 147 deletions tests/api_thread.rs
Original file line number Diff line number Diff line change
@@ -1,178 +1,119 @@
#![feature(maybe_uninit_write_slice)]

use std::{
mem::{MaybeUninit, zeroed},
sync::{Arc, LazyLock, Mutex, MutexGuard},
};

use axcpu::uspace::UserContext;
use extern_trait::extern_trait;
use kspin::SpinNoIrq;
use starry_signal::{
SignalDisposition, SignalInfo, SignalOSAction, SignalSet, Signo,
api::{ProcessSignalManager, SignalActions, ThreadSignalManager},
};
use starry_vm::{VmIo, VmResult};

struct TestEnv {
actions: Arc<SpinNoIrq<SignalActions>>,
proc: Arc<ProcessSignalManager>,
thr: Arc<ThreadSignalManager>,
}

impl TestEnv {
fn new() -> Self {
let actions = Arc::new(SpinNoIrq::new(SignalActions::default()));
let proc = Arc::new(ProcessSignalManager::new(actions.clone(), 0));
let thr = ThreadSignalManager::new(7, proc.clone());
Self { actions, proc, thr }
}
}
use starry_signal::{SignalDisposition, SignalInfo, SignalOSAction, SignalSet, Signo};

static POOL: LazyLock<Mutex<Box<[u8]>>> = LazyLock::new(|| {
let size = 0x0100_0000; // 1 MiB
Mutex::new(vec![0; size].into_boxed_slice())
});

struct Vm(MutexGuard<'static, Box<[u8]>>);

#[extern_trait]
unsafe impl VmIo for Vm {
fn new() -> Self {
let pool = POOL.lock().unwrap();
Vm(pool)
}

fn read(&mut self, start: usize, buf: &mut [MaybeUninit<u8>]) -> VmResult {
let base = self.0.as_ptr() as usize;
let offset = start - base;
let slice = &self.0[offset..offset + buf.len()];
buf.write_copy_of_slice(slice);
Ok(())
}

fn write(&mut self, start: usize, buf: &[u8]) -> VmResult {
let base = self.0.as_ptr() as usize;
let offset = start - base;
let slice = &mut self.0[offset..offset + buf.len()];
slice.copy_from_slice(buf);
Ok(())
}
}
mod common;
use common::*;

#[test]
fn block_ignore_send_signal() {
let env = TestEnv::new();
let actions = env.actions.clone();
let sig = SignalInfo::new_user(Signo::SIGINT, 0, 1);
assert!(env.thr.send_signal(sig.clone()));
fn dequeue_signal() {
let (proc, thr) = new_test_env();

actions.lock()[Signo::SIGINT].disposition = SignalDisposition::Ignore;
let proc_ignore = Arc::new(ProcessSignalManager::new(actions.clone(), 0));
let thr_ignore = ThreadSignalManager::new(7, proc_ignore.clone());
assert!(!thr_ignore.send_signal(sig.clone()));
let sig1 = SignalInfo::new_user(Signo::SIGINT, 9, 9);
assert!(thr.send_signal(sig1));

let mut set = SignalSet::default();
set.add(Signo::SIGINT);
env.thr.set_blocked(set);
assert!(!env.thr.send_signal(sig.clone()));
assert!(env.thr.pending().has(Signo::SIGINT));
assert!(env.thr.signal_blocked(Signo::SIGINT));
let sig2 = SignalInfo::new_user(Signo::SIGTERM, 9, 9);
assert_eq!(proc.send_signal(sig2), Some(TID));

let empty = SignalSet::default();
env.thr.set_blocked(empty);
assert!(!env.thr.signal_blocked(Signo::SIGINT));
let mask = !SignalSet::default();
assert_eq!(thr.dequeue_signal(&mask).unwrap().signo(), Signo::SIGINT);
assert_eq!(thr.dequeue_signal(&mask).unwrap().signo(), Signo::SIGTERM);
assert!(thr.dequeue_signal(&mask).is_none());
}

#[test]
fn handle_signal() {
let (proc, thr) = new_test_env();

let signo = Signo::SIGTERM;
let sig = SignalInfo::new_user(signo, 9, 9);

unsafe extern "C" fn test_handler(_: i32) {}
let env = TestEnv::new();
let actions = env.actions.clone();
actions.lock()[Signo::SIGTERM].disposition = SignalDisposition::Handler(test_handler);
let sig = SignalInfo::new_user(Signo::SIGTERM, 9, 9);

let mut uctx: UserContext = unsafe { zeroed() };
let initial_sp = {
let pool = POOL.lock().unwrap();
pool.as_ptr() as usize + pool.len()
};
uctx.set_sp(initial_sp);

let restore_blocked = env.thr.blocked();
let action = env.actions.lock()[sig.signo()].clone();
let result = env
.thr
.handle_signal(&mut uctx, restore_blocked, &sig, &action);

assert!(matches!(result, Some(SignalOSAction::Handler)));
proc.actions.lock()[signo].disposition = SignalDisposition::Handler(test_handler);

let initial = UserContext::new(0, initial_sp().into(), 0);

let mut uctx = initial;
let restore_blocked = thr.blocked();
let action = proc.actions.lock()[signo].clone();
let result = thr.handle_signal(&mut uctx, restore_blocked, &sig, &action);

assert_eq!(result, Some(SignalOSAction::Handler));
assert_eq!(uctx.ip(), test_handler as *const () as usize);
assert!(uctx.sp() < initial_sp);
assert_eq!(uctx.arg0(), Signo::SIGTERM as usize);
assert!(uctx.sp() < initial.sp());
assert_eq!(uctx.arg0(), signo as usize);
}

#[test]
fn dequeue_signal() {
let env = TestEnv::new();
let sig1 = SignalInfo::new_user(Signo::SIGINT, 9, 9);
let sig2 = SignalInfo::new_user(Signo::SIGTERM, 9, 9);
let mask = SignalSet::default();
let allowed_mask = !mask;
assert!(env.thr.send_signal(sig1.clone()));
assert_eq!(env.proc.send_signal(sig2), Some(7));
assert_eq!(
env.thr.dequeue_signal(&allowed_mask).unwrap().signo(),
Signo::SIGINT
);
fn block_ignore_send_signal() {
let (proc, thr) = new_test_env();

let signo = Signo::SIGINT;
let sig = SignalInfo::new_user(signo, 0, 1);
assert!(thr.send_signal(sig.clone()));
assert_eq!(
env.thr.dequeue_signal(&allowed_mask).unwrap().signo(),
Signo::SIGTERM
thr.dequeue_signal(&!SignalSet::default()).unwrap().signo(),
sig.signo()
);
assert!(env.thr.dequeue_signal(&allowed_mask).is_none());

proc.actions.lock()[signo].disposition = SignalDisposition::Ignore;
assert!(!thr.send_signal(sig.clone()));
assert!(!thr.pending().has(signo));

let mut set = SignalSet::default();
set.add(signo);
thr.set_blocked(set);
assert!(thr.signal_blocked(signo));
assert!(!thr.send_signal(sig.clone()));
assert!(!thr.pending().has(signo));

proc.actions.lock()[signo].disposition = SignalDisposition::Default;
assert!(!thr.send_signal(sig.clone()));
assert!(thr.pending().has(signo));

let empty = SignalSet::default();
thr.set_blocked(empty);
assert!(!thr.signal_blocked(signo));
}

#[test]
fn check_signals() {
let env = TestEnv::new();
let mut uctx: UserContext = unsafe { zeroed() };
uctx.set_sp(0x8000_0000);
let (proc, thr) = new_test_env();

let mut uctx = UserContext::new(0, 0.into(), 0);

let sig = SignalInfo::new_user(Signo::SIGTERM, 0, 1);
assert_eq!(env.proc.send_signal(sig.clone()), Some(7));
let signo = Signo::SIGTERM;
let sig = SignalInfo::new_user(signo, 0, 1);

let (si, _os_action) = env.thr.check_signals(&mut uctx, None).unwrap();
assert_eq!(si.signo(), Signo::SIGTERM);
assert_eq!(proc.send_signal(sig.clone()), Some(TID));
let (si, _os_action) = thr.check_signals(&mut uctx, None).unwrap();
assert_eq!(si.signo(), signo);

assert!(env.thr.send_signal(sig.clone()));
let (si, _os_action) = env.thr.check_signals(&mut uctx, None).unwrap();
assert_eq!(si.signo(), Signo::SIGTERM);
assert!(thr.send_signal(sig.clone()));
let (si, _os_action) = thr.check_signals(&mut uctx, None).unwrap();
assert_eq!(si.signo(), signo);
}

#[test]
fn restore() {
let (proc, thr) = new_test_env();

let signo = Signo::SIGTERM;
let sig = SignalInfo::new_user(signo, 0, 1);

unsafe extern "C" fn test_handler(_: i32) {}
let env = TestEnv::new();
let sig = SignalInfo::new_user(Signo::SIGTERM, 0, 1);
env.actions.lock()[Signo::SIGTERM].disposition = SignalDisposition::Handler(test_handler);

let sp = {
let pool = POOL.lock().unwrap();
pool.as_ptr() as usize + pool.len()
};
let mut initial: UserContext = unsafe { zeroed() };
initial.set_sp(sp);
initial.set_ip(0x219);
let mut uctx_user = initial.clone();

let restore_blocked = env.thr.blocked();
let action = env.actions.lock()[sig.signo()].clone();
env.thr
.handle_signal(&mut uctx_user, restore_blocked, &sig, &action);

let new_sp = uctx_user.sp() + 8;
uctx_user.set_sp(new_sp);
env.thr.restore(&mut uctx_user);

assert_eq!(uctx_user.ip(), initial.ip());
assert_eq!(uctx_user.sp(), initial.sp());
proc.actions.lock()[signo].disposition = SignalDisposition::Handler(test_handler);

let initial = UserContext::new(0x219, initial_sp().into(), 0);

let mut uctx = initial;
let restore_blocked = thr.blocked();
let action = proc.actions.lock()[sig.signo()].clone();
thr.handle_signal(&mut uctx, restore_blocked, &sig, &action);

let new_sp = uctx.sp() + 8;
uctx.set_sp(new_sp);
thr.restore(&mut uctx);

assert_eq!(uctx.ip(), initial.ip());
assert_eq!(uctx.sp(), initial.sp());
}
62 changes: 62 additions & 0 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::{
mem::MaybeUninit,
sync::{Arc, LazyLock, Mutex, MutexGuard},
};

use extern_trait::extern_trait;
use kspin::SpinNoIrq;
use starry_signal::api::{ProcessSignalManager, SignalActions, ThreadSignalManager};
use starry_vm::{VmError, VmIo, VmResult};

static POOL: LazyLock<Mutex<Box<[u8]>>> = LazyLock::new(|| {
let size = 0x0100_0000; // 16 MiB
Mutex::new(vec![0; size].into_boxed_slice())
});

pub fn initial_sp() -> usize {
let pool = POOL.lock().unwrap();
pool.as_ptr() as usize + pool.len()
}

struct Vm(MutexGuard<'static, Box<[u8]>>);

#[extern_trait]
unsafe impl VmIo for Vm {
fn new() -> Self {
let pool = POOL.lock().unwrap();
Vm(pool)
}

fn read(&mut self, start: usize, buf: &mut [MaybeUninit<u8>]) -> VmResult {
let base = self.0.as_ptr() as usize;
let offset = start.checked_sub(base).ok_or(VmError::BadAddress)?;
if offset.checked_add(buf.len()).ok_or(VmError::BadAddress)? > self.0.len() {
return Err(VmError::BadAddress);
}
let slice = &self.0[offset..offset + buf.len()];
buf.write_copy_of_slice(slice);
Ok(())
}

fn write(&mut self, start: usize, buf: &[u8]) -> VmResult {
let base = self.0.as_ptr() as usize;
let offset = start.checked_sub(base).ok_or(VmError::BadAddress)?;
if offset.checked_add(buf.len()).ok_or(VmError::BadAddress)? > self.0.len() {
return Err(VmError::BadAddress);
}
let slice = &mut self.0[offset..offset + buf.len()];
slice.copy_from_slice(buf);
Ok(())
}
}

pub const TID: u32 = 7;

pub fn new_test_env() -> (Arc<ProcessSignalManager>, Arc<ThreadSignalManager>) {
let proc = Arc::new(ProcessSignalManager::new(
Arc::new(SpinNoIrq::new(SignalActions::default())),
0,
));
let thr = ThreadSignalManager::new(TID, proc.clone());
(proc, thr)
}
Loading