Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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