Genericize `kill*` functions with `Signal` trait

This commit is contained in:
li041 2026-01-21 02:07:40 +00:00 committed by Tate, Hongliang Tian
parent 3d6d91ea18
commit 3ae286980e
8 changed files with 42 additions and 31 deletions

View File

@ -49,7 +49,7 @@ fn send_parent_death_signal(current_process: &Process) {
};
// FIXME: Set `si_pid` in the `siginfo_t` argument.
let signal = KernelSignal::new(signum);
let signal = Box::new(KernelSignal::new(signum));
child.enqueue_signal(signal);
}
}
@ -142,7 +142,7 @@ fn send_child_death_signal(current_process: &Process) {
};
if let Some(signal) = current_process.exit_signal().map(KernelSignal::new) {
parent.enqueue_signal(signal);
parent.enqueue_signal(Box::new(signal));
};
parent.children_wait_queue().wake_all();
}

View File

@ -4,11 +4,7 @@ use super::{
Pgid, Pid, Process,
posix_thread::{AsPosixThread, thread_table},
process_table,
signal::{
constants::SIGCONT,
sig_num::SigNum,
signals::{Signal, user::UserSignal},
},
signal::{constants::SIGCONT, sig_num::SigNum, signals::Signal},
};
use crate::{
prelude::*,
@ -23,8 +19,8 @@ use crate::{
///
/// If `signal` is `None`, this method will only check permission without sending
/// any signal.
pub fn kill(pid: Pid, signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
// Fast path: If the signal is sent to self, we can skip most check.
pub fn kill(pid: Pid, signal: Option<Box<dyn Signal>>, ctx: &Context) -> Result<()> {
// Fast path: If the signal is sent to self, we can skip most checks.
if pid == ctx.process.pid() {
let Some(signal) = signal else {
return Ok(());
@ -32,7 +28,7 @@ pub fn kill(pid: Pid, signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
if !ctx.posix_thread.has_signal_blocked(signal.num()) {
// Killing the current thread does not raise any permission issues.
ctx.posix_thread.enqueue_signal(Box::new(signal));
ctx.posix_thread.enqueue_signal(signal);
return Ok(());
}
@ -40,7 +36,6 @@ pub fn kill(pid: Pid, signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
}
// Slow path
let process = process_table::get_process(pid)
.ok_or_else(|| Error::with_message(Errno::ESRCH, "the target process does not exist"))?;
@ -55,7 +50,7 @@ pub fn kill(pid: Pid, signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
///
/// If `signal` is `None`, this method will only check permission without sending
/// any signal.
pub fn kill_group(pgid: Pgid, signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
pub fn kill_group<S: Signal + Clone>(pgid: Pgid, signal: Option<S>, ctx: &Context) -> Result<()> {
let process_group = process_table::get_process_group(&pgid)
.ok_or_else(|| Error::with_message(Errno::ESRCH, "the target group does not exist"))?;
@ -63,7 +58,11 @@ pub fn kill_group(pgid: Pgid, signal: Option<UserSignal>, ctx: &Context) -> Resu
let inner = process_group.lock();
for process in inner.iter() {
let res = kill_process(process, signal, ctx);
let res = kill_process(
process,
signal.clone().map(|s| Box::new(s) as Box<dyn Signal>),
ctx,
);
if res.is_err_and(|err| err.error() != Errno::EPERM) {
result = res;
}
@ -77,7 +76,7 @@ pub fn kill_group(pgid: Pgid, signal: Option<UserSignal>, ctx: &Context) -> Resu
///
/// If `signal` is `None`, this method will only check permission without sending
/// any signal.
pub fn tgkill(tid: Tid, tgid: Pid, signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
pub fn tgkill(tid: Tid, tgid: Pid, signal: Option<Box<dyn Signal>>, ctx: &Context) -> Result<()> {
let thread = thread_table::get_thread(tid)
.ok_or_else(|| Error::with_message(Errno::ESRCH, "the target thread does not exist"))?;
let target_posix_thread = thread.as_posix_thread().unwrap();
@ -92,7 +91,7 @@ pub fn tgkill(tid: Tid, tgid: Pid, signal: Option<UserSignal>, ctx: &Context) ->
}
// Check permission
let signum = signal.map(|signal| signal.num());
let signum = signal.as_ref().map(|signal| signal.num());
check_signal_perm(target_posix_thread, ctx, signum)?;
if thread.is_exited() {
@ -102,7 +101,7 @@ pub fn tgkill(tid: Tid, tgid: Pid, signal: Option<UserSignal>, ctx: &Context) ->
if let Some(signal) = signal {
// We've checked the permission issues above.
// FIXME: We should take some lock while checking the permission to avoid race conditions.
target_posix_thread.enqueue_signal(Box::new(signal));
target_posix_thread.enqueue_signal(signal);
}
Ok(())
@ -113,7 +112,7 @@ pub fn tgkill(tid: Tid, tgid: Pid, signal: Option<UserSignal>, ctx: &Context) ->
///
/// The credentials of the current process will be checked to determine
/// if it is authorized to send the signal to the target group.
pub fn kill_all(signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
pub fn kill_all<S: Signal + Clone>(signal: Option<S>, ctx: &Context) -> Result<()> {
let mut result = Ok(());
for process in process_table::process_table_mut().iter() {
@ -121,7 +120,11 @@ pub fn kill_all(signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
continue;
}
let res = kill_process(process, signal, ctx);
let res = kill_process(
process,
signal.clone().map(|s| Box::new(s) as Box<dyn Signal>),
ctx,
);
if res.is_err_and(|err| err.error() != Errno::EPERM) {
result = res;
}
@ -130,8 +133,8 @@ pub fn kill_all(signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
result
}
fn kill_process(process: &Process, signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
let signum = signal.map(|signal| signal.num());
fn kill_process(process: &Process, signal: Option<Box<dyn Signal>>, ctx: &Context) -> Result<()> {
let signum = signal.as_ref().map(|signal| signal.num());
let target_main_thread = process.main_thread();
check_signal_perm(target_main_thread.as_posix_thread().unwrap(), ctx, signum)?;

View File

@ -634,12 +634,12 @@ impl Process {
/// This method does not perform permission checks on user signals.
/// Therefore, unless the caller can ensure that there are no permission issues,
/// this method should be used to enqueue kernel signals or fault signals.
pub fn enqueue_signal(&self, signal: impl Signal + Clone + 'static) {
pub fn enqueue_signal(&self, signal: Box<dyn Signal>) {
if self.status.is_zombie() {
return;
}
self.sig_queues.enqueue(Box::new(signal));
self.sig_queues.enqueue(signal);
for task in self.tasks.lock().as_slice() {
let posix_thread = task.as_posix_thread().unwrap();
@ -819,7 +819,7 @@ pub fn enqueue_signal_async(process: Weak<Process>, signum: SigNum) {
work_queue::submit_work_func(
move || {
if let Some(process) = process.upgrade() {
process.enqueue_signal(KernelSignal::new(signum));
process.enqueue_signal(Box::new(KernelSignal::new(signum)));
}
},
work_queue::WorkPriority::High,

View File

@ -66,7 +66,7 @@ impl ProcessGroup {
// TODO: Do some checks to forbid user signals.
pub fn broadcast_signal(&self, signal: impl Signal + Clone + 'static) {
for process in self.inner.lock().processes.values() {
process.enqueue_signal(signal.clone());
process.enqueue_signal(Box::new(signal.clone()));
}
}
}

View File

@ -107,7 +107,7 @@ fn create_process_timer_callback(
let sent_signal = move || {
let signal = KernelSignal::new(SIGALRM);
if let Some(process) = current_process.upgrade() {
process.enqueue_signal(signal);
process.enqueue_signal(Box::new(signal));
}
};

View File

@ -7,7 +7,10 @@ use crate::{
ProcessFilter, kill, kill_all, kill_group,
signal::{
sig_num::SigNum,
signals::user::{UserSignal, UserSignalKind},
signals::{
Signal,
user::{UserSignal, UserSignalKind},
},
},
},
};
@ -36,7 +39,9 @@ pub fn do_sys_kill(filter: ProcessFilter, sig_num: Option<SigNum>, ctx: &Context
match filter {
ProcessFilter::Any => kill_all(signal, ctx)?,
ProcessFilter::WithPid(pid) => kill(pid, signal, ctx)?,
ProcessFilter::WithPid(pid) => {
kill(pid, signal.map(|s| Box::new(s) as Box<dyn Signal>), ctx)?
}
ProcessFilter::WithPgid(pgid) => kill_group(pgid, signal, ctx)?,
ProcessFilter::WithPidfd(_) => unreachable!(),
}

View File

@ -7,7 +7,10 @@ use crate::{
Pid,
signal::{
sig_num::SigNum,
signals::user::{UserSignal, UserSignalKind},
signals::{
Signal,
user::{UserSignal, UserSignalKind},
},
},
tgkill,
},
@ -30,7 +33,7 @@ pub fn sys_tgkill(tgid: Pid, tid: Tid, sig_num: u8, ctx: &Context) -> Result<Sys
let signal = sig_num.map(|sig_num| {
let pid = ctx.process.pid();
let uid = ctx.posix_thread.credentials().ruid();
UserSignal::new(sig_num, UserSignalKind::Tkill, pid, uid)
Box::new(UserSignal::new(sig_num, UserSignalKind::Tkill, pid, uid)) as Box<dyn Signal>
});
tgkill(tid, tgid, signal, ctx)?;
Ok(SyscallReturn::Return(0))

View File

@ -47,7 +47,7 @@ pub fn sys_timer_create(
let process = current_process.clone();
let signal = KernelSignal::new(SIGALRM);
Box::new(move || {
process.enqueue_signal(signal);
process.enqueue_signal(Box::new(signal));
})
// Determine the timeout action through `sigevent`.
} else {
@ -62,7 +62,7 @@ pub fn sys_timer_create(
let process = current_process.clone();
let signal = KernelSignal::new(SigNum::try_from(signo as u8)?);
Box::new(move || {
process.enqueue_signal(signal);
process.enqueue_signal(Box::new(signal));
})
}
// Spawn a posix thread to run the `sigev_function`, which is stored in