asterinas/kernel/src/process/kill.rs

213 lines
6.6 KiB
Rust
Raw Normal View History

2024-01-03 03:22:36 +00:00
// SPDX-License-Identifier: MPL-2.0
use super::{
posix_thread::{thread_table, AsPosixThread},
process_table,
signal::{
constants::SIGCONT,
sig_num::SigNum,
signals::{user::UserSignal, Signal},
},
Pgid, Pid, Process, Sid, Uid,
};
use crate::{prelude::*, thread::Tid};
2023-11-29 11:43:17 +00:00
/// Sends a signal to a process, using the current process as the sender.
///
/// The credentials of the current process will be checked to determine
/// if it is authorized to send the signal to this particular target process.
///
/// 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.
if pid == ctx.process.pid() {
let Some(signal) = signal else {
return Ok(());
};
if !ctx.posix_thread.has_signal_blocked(signal.num()) {
2025-05-12 03:22:47 +00:00
// Killing the current thread does not raise any permission issues.
ctx.posix_thread.enqueue_signal(Box::new(signal));
return Ok(());
}
return kill_process(ctx.process, Some(signal), ctx);
}
// Slow path
2024-05-31 10:21:46 +00:00
let process = process_table::get_process(pid)
2023-11-29 11:43:17 +00:00
.ok_or_else(|| Error::with_message(Errno::ESRCH, "the target process does not exist"))?;
kill_process(&process, signal, ctx)
2023-11-29 11:43:17 +00:00
}
/// Sends a signal to all processes in a group, using the current process
/// as the sender.
///
/// The credentials of the current process will be checked to determine
/// if it is authorized to send the signal to the target group.
///
/// 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<()> {
2023-11-29 11:43:17 +00:00
let process_group = process_table::get_process_group(&pgid)
.ok_or_else(|| Error::with_message(Errno::ESRCH, "target group does not exist"))?;
2025-04-21 06:17:02 +00:00
let inner = process_group.lock();
for process in inner.iter() {
kill_process(process, signal, ctx)?;
2023-11-29 11:43:17 +00:00
}
Ok(())
}
/// Sends a signal to a target thread, using the current process
/// as the sender.
///
/// 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<()> {
let thread = thread_table::get_thread(tid)
2023-11-29 11:43:17 +00:00
.ok_or_else(|| Error::with_message(Errno::ESRCH, "target thread does not exist"))?;
if thread.is_exited() {
2023-11-29 11:43:17 +00:00
return Ok(());
}
let posix_thread = thread.as_posix_thread().unwrap();
// Check tgid
let pid = posix_thread.process().pid();
if pid != tgid {
return_errno_with_message!(
Errno::EINVAL,
"the combination of tgid and pid is not valid"
);
}
// Check permission
let signum = signal.map(|signal| signal.num());
let sender = current_thread_sender_ids(signum.as_ref(), ctx);
2023-11-29 11:43:17 +00:00
posix_thread.check_signal_perm(signum.as_ref(), &sender)?;
if let Some(signal) = signal {
2025-05-12 03:22:47 +00:00
// We've checked the permission issues above.
// FIXME: We should take some lock while checking the permission to avoid race conditions.
2023-11-29 11:43:17 +00:00
posix_thread.enqueue_signal(Box::new(signal));
}
Ok(())
}
/// Sends a signal to all processes except current process and init process, using
/// the current process as the sender.
///
/// 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<()> {
2023-11-29 11:43:17 +00:00
let current = current!();
2024-10-27 06:47:39 +00:00
for process in process_table::process_table_mut().iter() {
if Arc::ptr_eq(&current, process) || process.is_init_process() {
2023-11-29 11:43:17 +00:00
continue;
}
kill_process(process, signal, ctx)?;
2023-11-29 11:43:17 +00:00
}
Ok(())
}
fn kill_process(process: &Process, signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
2025-05-12 03:22:47 +00:00
let sig_dispositions = process.sig_dispositions().lock();
let tasks = process.tasks().lock();
2023-11-29 11:43:17 +00:00
let signum = signal.map(|signal| signal.num());
let sender_ids = current_thread_sender_ids(signum.as_ref(), ctx);
let mut permitted_thread = None;
2024-11-23 14:08:38 +00:00
for task in tasks.as_slice() {
let posix_thread = task.as_posix_thread().unwrap();
// First check permission
if posix_thread
.check_signal_perm(signum.as_ref(), &sender_ids)
.is_ok()
{
let Some(ref signum) = signum else {
2025-05-12 03:22:47 +00:00
// If `signal` is `None`, only permission check is required.
return Ok(());
};
if !posix_thread.has_signal_blocked(*signum) {
2025-05-12 03:22:47 +00:00
// Send the signal to any thread that does not block the signal.
permitted_thread = Some(posix_thread);
break;
} else if permitted_thread.is_none() {
2025-05-12 03:22:47 +00:00
// If all threads block the signal, send the signal to the first permitted thread.
permitted_thread = Some(posix_thread);
}
}
}
2023-11-29 11:43:17 +00:00
let Some(permitted_thread) = permitted_thread else {
2023-11-29 11:43:17 +00:00
return_errno_with_message!(Errno::EPERM, "cannot send signal to the target process");
};
2023-11-29 11:43:17 +00:00
2025-05-12 03:22:47 +00:00
// Since `permitted_thread` has been set, `signal` cannot be `None`.
let signal = signal.unwrap();
// Drop the signal if it's ignored. See explanation at `enqueue_signal_locked`.
let signum = signal.num();
if sig_dispositions.get(signum).will_ignore(signum) {
return Ok(());
}
2023-11-29 11:43:17 +00:00
2025-05-12 03:22:47 +00:00
permitted_thread.enqueue_signal_locked(Box::new(signal), sig_dispositions);
2023-11-29 11:43:17 +00:00
Ok(())
}
fn current_thread_sender_ids(signum: Option<&SigNum>, ctx: &Context) -> SignalSenderIds {
let credentials = ctx.posix_thread.credentials();
2023-11-29 11:43:17 +00:00
let ruid = credentials.ruid();
let euid = credentials.euid();
let sid = signum.and_then(|signum| {
if *signum == SIGCONT {
2025-04-21 15:51:16 +00:00
Some(ctx.process.sid())
} else {
None
}
});
2023-11-29 11:43:17 +00:00
SignalSenderIds::new(ruid, euid, sid)
}
/// The ids of the signal sender process.
///
/// This struct now includes effective user id, real user id and session id.
pub(super) struct SignalSenderIds {
ruid: Uid,
euid: Uid,
sid: Option<Sid>,
2023-11-29 11:43:17 +00:00
}
impl SignalSenderIds {
fn new(ruid: Uid, euid: Uid, sid: Option<Sid>) -> Self {
2023-11-29 11:43:17 +00:00
Self { ruid, euid, sid }
}
pub(super) fn ruid(&self) -> Uid {
self.ruid
}
pub(super) fn euid(&self) -> Uid {
self.euid
}
pub(super) fn sid(&self) -> Option<Sid> {
2023-11-29 11:43:17 +00:00
self.sid
}
}