Add `pidfd_send_signal` syscall
This commit is contained in:
parent
3ae286980e
commit
84bced252b
|
|
@ -343,6 +343,7 @@ which are summarized in the table below.
|
||||||
| 327 | preadv2 | ✅ | [⚠️](syscall-flag-coverage/file-and-directory-operations/#preadv2-and-pwritev2) |
|
| 327 | preadv2 | ✅ | [⚠️](syscall-flag-coverage/file-and-directory-operations/#preadv2-and-pwritev2) |
|
||||||
| 328 | pwritev2 | ✅ | [⚠️](syscall-flag-coverage/file-and-directory-operations/#preadv2-and-pwritev2) |
|
| 328 | pwritev2 | ✅ | [⚠️](syscall-flag-coverage/file-and-directory-operations/#preadv2-and-pwritev2) |
|
||||||
| 332 | statx | ✅ | [⚠️](syscall-flag-coverage/file-and-directory-operations/#statx) |
|
| 332 | statx | ✅ | [⚠️](syscall-flag-coverage/file-and-directory-operations/#statx) |
|
||||||
|
| 424 | pidfd_send_signal | ✅ | 💯 |
|
||||||
| 434 | pidfd_open | ✅ | 💯 |
|
| 434 | pidfd_open | ✅ | 💯 |
|
||||||
| 435 | clone3 | ✅ | [⚠️](syscall-flag-coverage/process-and-thread-management/#clone-and-clone3) |
|
| 435 | clone3 | ✅ | [⚠️](syscall-flag-coverage/process-and-thread-management/#clone-and-clone3) |
|
||||||
| 436 | close_range | ✅ | 💯 |
|
| 436 | close_range | ✅ | 💯 |
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ select(nfds, readfds, writefds, exceptfds, timeout);
|
||||||
pidfd_open(pid, flags = PIDFD_NONBLOCK);
|
pidfd_open(pid, flags = PIDFD_NONBLOCK);
|
||||||
|
|
||||||
// Obtain a duplicate of another process's file descriptor
|
// Obtain a duplicate of another process's file descriptor
|
||||||
pidfd_getfd(pid, targetfd, flags = 0);
|
pidfd_getfd(pidfd, targetfd, flags = 0);
|
||||||
|
|
||||||
// Close all file descriptors in the inclusive range [first, last]
|
// Close all file descriptors in the inclusive range [first, last]
|
||||||
close_range(first, last, flags = CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC);
|
close_range(first, last, flags = CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC);
|
||||||
|
|
@ -16,6 +16,9 @@ kill(pid, sig);
|
||||||
// Send signal to a thread
|
// Send signal to a thread
|
||||||
tgkill(tgid, tid, sig);
|
tgkill(tgid, tid, sig);
|
||||||
|
|
||||||
|
// Send signal to the target process referred to by pidfd
|
||||||
|
pidfd_send_signal(pidfd, sig, info, flags = PIDFD_SIGNAL_THREAD | PIDFD_SIGNAL_THREAD_GROUP | PIDFD_SIGNAL_PROCESS_GROUP);
|
||||||
|
|
||||||
// Wait for signal
|
// Wait for signal
|
||||||
pause();
|
pause();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
pub mod fault;
|
pub mod fault;
|
||||||
pub mod kernel;
|
pub mod kernel;
|
||||||
|
pub mod raw;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
|
||||||
use core::{any::Any, fmt::Debug};
|
use core::{any::Any, fmt::Debug};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use core::fmt::Debug;
|
||||||
|
|
||||||
|
use crate::process::signal::{c_types::siginfo_t, sig_num::SigNum, signals::Signal};
|
||||||
|
|
||||||
|
/// A signal that carries raw [`siginfo_t`] information.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct RawSignal {
|
||||||
|
info: siginfo_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for RawSignal {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("RawSignal")
|
||||||
|
.field("signo", &self.info.si_signo)
|
||||||
|
.field("errno", &self.info.si_errno)
|
||||||
|
.field("code", &self.info.si_code)
|
||||||
|
.finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawSignal {
|
||||||
|
/// Creates a signal that carries raw [`siginfo_t`] information.
|
||||||
|
///
|
||||||
|
/// The caller must ensure that the `info.si_signo` is a valid signal number.
|
||||||
|
pub fn new(info: siginfo_t) -> Self {
|
||||||
|
Self { info }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Signal for RawSignal {
|
||||||
|
fn num(&self) -> SigNum {
|
||||||
|
SigNum::from_u8(self.info.si_signo as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_info(&self) -> siginfo_t {
|
||||||
|
self.info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,12 +3,15 @@
|
||||||
#![expect(dead_code)]
|
#![expect(dead_code)]
|
||||||
|
|
||||||
use super::Signal;
|
use super::Signal;
|
||||||
use crate::process::{
|
use crate::{
|
||||||
Pid, Uid,
|
context::Context,
|
||||||
signal::{
|
process::{
|
||||||
c_types::siginfo_t,
|
Pid, Uid,
|
||||||
constants::{SI_QUEUE, SI_TKILL, SI_USER},
|
signal::{
|
||||||
sig_num::SigNum,
|
c_types::siginfo_t,
|
||||||
|
constants::{SI_QUEUE, SI_TKILL, SI_USER},
|
||||||
|
sig_num::SigNum,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -37,6 +40,15 @@ impl UserSignal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_kill(num: SigNum, ctx: &Context) -> Self {
|
||||||
|
Self {
|
||||||
|
num,
|
||||||
|
kind: UserSignalKind::Kill,
|
||||||
|
pid: ctx.process.pid(),
|
||||||
|
uid: ctx.posix_thread.credentials().ruid(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pid(&self) -> Pid {
|
pub fn pid(&self) -> Pid {
|
||||||
self.pid
|
self.pid
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ macro_rules! import_generic_syscall_entries {
|
||||||
open::sys_openat,
|
open::sys_openat,
|
||||||
pidfd_getfd::sys_pidfd_getfd,
|
pidfd_getfd::sys_pidfd_getfd,
|
||||||
pidfd_open::sys_pidfd_open,
|
pidfd_open::sys_pidfd_open,
|
||||||
|
pidfd_send_signal::sys_pidfd_send_signal,
|
||||||
pipe::sys_pipe2,
|
pipe::sys_pipe2,
|
||||||
ppoll::sys_ppoll,
|
ppoll::sys_ppoll,
|
||||||
prctl::sys_prctl,
|
prctl::sys_prctl,
|
||||||
|
|
@ -390,6 +391,7 @@ macro_rules! define_syscalls_with_generic_syscall_table {
|
||||||
SYS_PREADV2 = 286 => sys_preadv2(args[..6]);
|
SYS_PREADV2 = 286 => sys_preadv2(args[..6]);
|
||||||
SYS_PWRITEV2 = 287 => sys_pwritev2(args[..6]);
|
SYS_PWRITEV2 = 287 => sys_pwritev2(args[..6]);
|
||||||
SYS_STATX = 291 => sys_statx(args[..5]);
|
SYS_STATX = 291 => sys_statx(args[..5]);
|
||||||
|
SYS_PIDFD_SEND_SIGNAL = 424 => sys_pidfd_send_signal(args[..4]);
|
||||||
SYS_PIDFD_OPEN = 434 => sys_pidfd_open(args[..2]);
|
SYS_PIDFD_OPEN = 434 => sys_pidfd_open(args[..2]);
|
||||||
SYS_CLONE3 = 435 => sys_clone3(args[..2], &user_ctx);
|
SYS_CLONE3 = 435 => sys_clone3(args[..2], &user_ctx);
|
||||||
SYS_CLOSE_RANGE = 436 => sys_close_range(args[..3]);
|
SYS_CLOSE_RANGE = 436 => sys_close_range(args[..3]);
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ use super::{
|
||||||
pause::sys_pause,
|
pause::sys_pause,
|
||||||
pidfd_getfd::sys_pidfd_getfd,
|
pidfd_getfd::sys_pidfd_getfd,
|
||||||
pidfd_open::sys_pidfd_open,
|
pidfd_open::sys_pidfd_open,
|
||||||
|
pidfd_send_signal::sys_pidfd_send_signal,
|
||||||
pipe::{sys_pipe, sys_pipe2},
|
pipe::{sys_pipe, sys_pipe2},
|
||||||
poll::sys_poll,
|
poll::sys_poll,
|
||||||
ppoll::sys_ppoll,
|
ppoll::sys_ppoll,
|
||||||
|
|
@ -405,6 +406,7 @@ impl_syscall_nums_and_dispatch_fn! {
|
||||||
SYS_PREADV2 = 327 => sys_preadv2(args[..6]);
|
SYS_PREADV2 = 327 => sys_preadv2(args[..6]);
|
||||||
SYS_PWRITEV2 = 328 => sys_pwritev2(args[..6]);
|
SYS_PWRITEV2 = 328 => sys_pwritev2(args[..6]);
|
||||||
SYS_STATX = 332 => sys_statx(args[..5]);
|
SYS_STATX = 332 => sys_statx(args[..5]);
|
||||||
|
SYS_PIDFD_SEND_SIGNAL = 424 => sys_pidfd_send_signal(args[..4]);
|
||||||
SYS_PIDFD_OPEN = 434 => sys_pidfd_open(args[..2]);
|
SYS_PIDFD_OPEN = 434 => sys_pidfd_open(args[..2]);
|
||||||
SYS_CLONE3 = 435 => sys_clone3(args[..2], &user_ctx);
|
SYS_CLONE3 = 435 => sys_clone3(args[..2], &user_ctx);
|
||||||
SYS_CLOSE_RANGE = 436 => sys_close_range(args[..3]);
|
SYS_CLOSE_RANGE = 436 => sys_close_range(args[..3]);
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,7 @@ use crate::{
|
||||||
ProcessFilter, kill, kill_all, kill_group,
|
ProcessFilter, kill, kill_all, kill_group,
|
||||||
signal::{
|
signal::{
|
||||||
sig_num::SigNum,
|
sig_num::SigNum,
|
||||||
signals::{
|
signals::{Signal, user::UserSignal},
|
||||||
Signal,
|
|
||||||
user::{UserSignal, UserSignalKind},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -31,11 +28,7 @@ pub fn sys_kill(process_filter: u64, sig_num: u64, ctx: &Context) -> Result<Sysc
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_sys_kill(filter: ProcessFilter, sig_num: Option<SigNum>, ctx: &Context) -> Result<()> {
|
pub fn do_sys_kill(filter: ProcessFilter, sig_num: Option<SigNum>, ctx: &Context) -> Result<()> {
|
||||||
let signal = sig_num.map(|sig_num| {
|
let signal = sig_num.map(|sig_num| UserSignal::new_kill(sig_num, ctx));
|
||||||
let pid = ctx.process.pid();
|
|
||||||
let uid = ctx.posix_thread.credentials().ruid();
|
|
||||||
UserSignal::new(sig_num, UserSignalKind::Kill, pid, uid)
|
|
||||||
});
|
|
||||||
|
|
||||||
match filter {
|
match filter {
|
||||||
ProcessFilter::Any => kill_all(signal, ctx)?,
|
ProcessFilter::Any => kill_all(signal, ctx)?,
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ mod open;
|
||||||
mod pause;
|
mod pause;
|
||||||
mod pidfd_getfd;
|
mod pidfd_getfd;
|
||||||
mod pidfd_open;
|
mod pidfd_open;
|
||||||
|
mod pidfd_send_signal;
|
||||||
mod pipe;
|
mod pipe;
|
||||||
mod poll;
|
mod poll;
|
||||||
mod ppoll;
|
mod ppoll;
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ pub fn sys_pidfd_getfd(
|
||||||
let mut file_table = ctx.thread_local.borrow_file_table_mut();
|
let mut file_table = ctx.thread_local.borrow_file_table_mut();
|
||||||
let file = get_file_fast!(&mut file_table, pidfd);
|
let file = get_file_fast!(&mut file_table, pidfd);
|
||||||
let Some(pid_file) = file.downcast_ref::<PidFile>() else {
|
let Some(pid_file) = file.downcast_ref::<PidFile>() else {
|
||||||
return_errno_with_message!(Errno::EINVAL, "the file is not a PID file");
|
return_errno_with_message!(Errno::EBADF, "the file is not a PID file");
|
||||||
};
|
};
|
||||||
|
|
||||||
let process = pid_file
|
let process = pid_file
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use ostd::mm::VmIo;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
fs::file_table::{FileDesc, get_file_fast},
|
||||||
|
prelude::*,
|
||||||
|
process::{
|
||||||
|
Pgid, Pid, PidFile, kill, kill_group,
|
||||||
|
posix_thread::AsPosixThread,
|
||||||
|
signal::{
|
||||||
|
c_types::siginfo_t,
|
||||||
|
constants::SI_TKILL,
|
||||||
|
sig_num::SigNum,
|
||||||
|
signals::{Signal, raw::RawSignal, user::UserSignal},
|
||||||
|
},
|
||||||
|
tgkill,
|
||||||
|
},
|
||||||
|
syscall::SyscallReturn,
|
||||||
|
thread::Tid,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn sys_pidfd_send_signal(
|
||||||
|
pidfd: FileDesc,
|
||||||
|
sig_num: u64,
|
||||||
|
info_ptr: Vaddr,
|
||||||
|
flags: u32,
|
||||||
|
ctx: &Context,
|
||||||
|
) -> Result<SyscallReturn> {
|
||||||
|
let flags = PidfdSendSignalFlags::try_from(flags)?;
|
||||||
|
let sig_num = SigNum::try_from(sig_num as u8)?;
|
||||||
|
debug!(
|
||||||
|
"pidfd={}, info_ptr={:#x}, flags={:?}",
|
||||||
|
pidfd, info_ptr, flags
|
||||||
|
);
|
||||||
|
|
||||||
|
let siginfo = read_siginfo_from_user(info_ptr, sig_num, ctx)?;
|
||||||
|
let signal = RawSignal::new(siginfo);
|
||||||
|
|
||||||
|
let target = get_target_from_pidfd(pidfd, flags, ctx)?;
|
||||||
|
|
||||||
|
let is_self = match &target {
|
||||||
|
SignalTarget::Thread { tid, tgid: _ } => *tid == ctx.posix_thread.tid(),
|
||||||
|
SignalTarget::Process { pid } => *pid == ctx.posix_thread.tid(),
|
||||||
|
SignalTarget::ProcessGroup { pgid: _ } => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if !is_self && (siginfo.si_code >= 0 || siginfo.si_code == SI_TKILL) {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EPERM,
|
||||||
|
"signals with custom code can only be sent to the current thread/process"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
match target {
|
||||||
|
SignalTarget::Thread { tid, tgid } => {
|
||||||
|
tgkill(tid, tgid, Some(Box::new(signal) as Box<dyn Signal>), ctx)?;
|
||||||
|
}
|
||||||
|
SignalTarget::Process { pid } => {
|
||||||
|
kill(pid, Some(Box::new(signal) as Box<dyn Signal>), ctx)?;
|
||||||
|
}
|
||||||
|
SignalTarget::ProcessGroup { pgid } => {
|
||||||
|
kill_group(pgid, Some(signal), ctx)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(SyscallReturn::Return(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_siginfo_from_user(info_ptr: Vaddr, sig_num: SigNum, ctx: &Context) -> Result<siginfo_t> {
|
||||||
|
if info_ptr != 0 {
|
||||||
|
let si = ctx.user_space().read_val::<siginfo_t>(info_ptr)?;
|
||||||
|
if si.si_signo != sig_num.as_u8() as i32 {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EINVAL,
|
||||||
|
"`siginfo.si_signo` does not match the specified signal number"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(si)
|
||||||
|
} else {
|
||||||
|
// If `info_ptr` is NULL, the kernel constructs a default `siginfo_t` structure
|
||||||
|
// whose fields match the values that are implicitly supplied when a signal is sent using the kill(2).
|
||||||
|
Ok(UserSignal::new_kill(sig_num, ctx).to_info())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_target_from_pidfd(
|
||||||
|
pidfd: FileDesc,
|
||||||
|
flags: PidfdSendSignalFlags,
|
||||||
|
ctx: &Context,
|
||||||
|
) -> Result<SignalTarget> {
|
||||||
|
// Helper closures to create signal targets.
|
||||||
|
let thread_target = |tid: Tid, tgid: Pid| SignalTarget::Thread { tid, tgid };
|
||||||
|
let process_target = |pid: Pid| SignalTarget::Process { pid };
|
||||||
|
let group_target = |pgid: Pgid| SignalTarget::ProcessGroup { pgid };
|
||||||
|
|
||||||
|
let target = match pidfd {
|
||||||
|
PIDFD_SELF_THREAD => match flags {
|
||||||
|
PidfdSendSignalFlags::Default | PidfdSendSignalFlags::Thread => {
|
||||||
|
thread_target(ctx.posix_thread.tid(), ctx.process.pid())
|
||||||
|
}
|
||||||
|
PidfdSendSignalFlags::ThreadGroup => process_target(ctx.process.pid()),
|
||||||
|
PidfdSendSignalFlags::ProcessGroup => group_target(ctx.posix_thread.tid()),
|
||||||
|
},
|
||||||
|
PIDFD_SELF_THREAD_GROUP => match flags {
|
||||||
|
PidfdSendSignalFlags::Default | PidfdSendSignalFlags::ThreadGroup => {
|
||||||
|
process_target(ctx.process.pid())
|
||||||
|
}
|
||||||
|
PidfdSendSignalFlags::Thread => thread_target(
|
||||||
|
ctx.process.main_thread().as_posix_thread().unwrap().tid(),
|
||||||
|
ctx.process.pid(),
|
||||||
|
),
|
||||||
|
PidfdSendSignalFlags::ProcessGroup => group_target(ctx.process.pid()),
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
let mut file_table = ctx.thread_local.borrow_file_table_mut();
|
||||||
|
let file = get_file_fast!(&mut file_table, pidfd);
|
||||||
|
|
||||||
|
// FIXME: On Linux, a pidfd can be also obtained by opening a `/proc/pid` directory.
|
||||||
|
// Reference: <https://man7.org/linux/man-pages/man2/pidfd_send_signal.2.html>
|
||||||
|
let Some(pid_file) = file.downcast_ref::<PidFile>() else {
|
||||||
|
return_errno_with_message!(Errno::EBADF, "the file is not a PID file");
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(process) = pid_file.process_opt() else {
|
||||||
|
return_errno_with_message!(Errno::ESRCH, "the target process has been reaped");
|
||||||
|
};
|
||||||
|
|
||||||
|
match flags {
|
||||||
|
PidfdSendSignalFlags::Default => {
|
||||||
|
// FIXME: On Linux, a pidfd can refer to either a process or a thread.
|
||||||
|
// We currently only support pidfds that refer to processes.
|
||||||
|
process_target(process.pid())
|
||||||
|
}
|
||||||
|
PidfdSendSignalFlags::Thread => {
|
||||||
|
// FIXME: On Linux, the signal can be sent to any thread.
|
||||||
|
// We currently only support the main thread.
|
||||||
|
thread_target(
|
||||||
|
process.main_thread().as_posix_thread().unwrap().tid(),
|
||||||
|
process.pid(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
PidfdSendSignalFlags::ThreadGroup => process_target(process.pid()),
|
||||||
|
PidfdSendSignalFlags::ProcessGroup => group_target(process.pid()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SignalTarget {
|
||||||
|
Thread { tid: Tid, tgid: Pid },
|
||||||
|
Process { pid: Pid },
|
||||||
|
ProcessGroup { pgid: Pgid },
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/linux/pidfd.h#L19>.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, TryFromInt)]
|
||||||
|
#[repr(u32)]
|
||||||
|
enum PidfdSendSignalFlags {
|
||||||
|
Default = 0x0,
|
||||||
|
Thread = 0x1,
|
||||||
|
ThreadGroup = 0x2,
|
||||||
|
ProcessGroup = 0x4,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/linux/fcntl.h#L110>.
|
||||||
|
const PIDFD_SELF_THREAD: i32 = -10000;
|
||||||
|
const PIDFD_SELF_THREAD_GROUP: i32 = -10001;
|
||||||
|
|
@ -70,6 +70,7 @@ sched/sched_param_idle
|
||||||
shm/posix_shm
|
shm/posix_shm
|
||||||
signal_c/kill
|
signal_c/kill
|
||||||
signal_c/parent_death_signal
|
signal_c/parent_death_signal
|
||||||
|
signal_c/pidfd_send_signal
|
||||||
signal_c/sigaltstack
|
signal_c/sigaltstack
|
||||||
signal_c/signal_fd
|
signal_c/signal_fd
|
||||||
signal_c/signal_fpu
|
signal_c/signal_fpu
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,260 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "../test.h"
|
||||||
|
|
||||||
|
// Reference: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/linux/fcntl.h#L110>.
|
||||||
|
#define PIDFD_SELF_THREAD -10000
|
||||||
|
#define PIDFD_SELF_THREAD_GROUP -10001
|
||||||
|
|
||||||
|
// Reference: <https://elixir.bootlin.com/linux/v6.18/source/include/uapi/linux/pidfd.h#L20>
|
||||||
|
#define PIDFD_SIGNAL_THREAD_GROUP (1UL << 1)
|
||||||
|
#define PIDFD_SIGNAL_PROCESS_GROUP (1UL << 2)
|
||||||
|
|
||||||
|
const int sig = SIGUSR1;
|
||||||
|
siginfo_t siginfo;
|
||||||
|
|
||||||
|
static int pidfd_open(pid_t pid, unsigned int flags)
|
||||||
|
{
|
||||||
|
return syscall(SYS_pidfd_open, pid, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
|
||||||
|
unsigned int flags)
|
||||||
|
{
|
||||||
|
return syscall(SYS_pidfd_send_signal, pidfd, sig, info, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_test_siginfo(siginfo_t *info, int sig, int si_code)
|
||||||
|
{
|
||||||
|
memset(info, 0, sizeof(*info));
|
||||||
|
info->si_signo = sig;
|
||||||
|
info->si_code = si_code;
|
||||||
|
info->si_pid = getpid();
|
||||||
|
info->si_uid = getuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================
|
||||||
|
* Tests for processes
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
int process_pid;
|
||||||
|
int process_pidfd;
|
||||||
|
|
||||||
|
FN_SETUP(create_process)
|
||||||
|
{
|
||||||
|
process_pid = CHECK(fork());
|
||||||
|
if (process_pid == 0) {
|
||||||
|
while (1) {
|
||||||
|
usleep(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process_pidfd = CHECK(pidfd_open(process_pid, 0));
|
||||||
|
}
|
||||||
|
END_SETUP()
|
||||||
|
|
||||||
|
FN_TEST(pidfd_send_signal_errnos)
|
||||||
|
{
|
||||||
|
setup_test_siginfo(&siginfo, sig, SI_USER);
|
||||||
|
TEST_ERRNO(pidfd_send_signal(process_pidfd, sig, &siginfo,
|
||||||
|
PIDFD_SIGNAL_PROCESS_GROUP),
|
||||||
|
EPERM);
|
||||||
|
|
||||||
|
setup_test_siginfo(&siginfo, sig, -666);
|
||||||
|
TEST_ERRNO(pidfd_send_signal(process_pidfd, sig + 1, &siginfo, 0),
|
||||||
|
EINVAL);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_TEST(pidfd_send_signal_process)
|
||||||
|
{
|
||||||
|
TEST_SUCC(pidfd_send_signal(process_pidfd, sig, &siginfo, 0));
|
||||||
|
TEST_SUCC(waitid(P_PID, process_pid, NULL, WNOWAIT | WEXITED));
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_SETUP(cleanup_process)
|
||||||
|
{
|
||||||
|
CHECK(close(process_pidfd));
|
||||||
|
}
|
||||||
|
END_SETUP()
|
||||||
|
|
||||||
|
/* ==========================
|
||||||
|
* Tests for `PIDFD_SELF_*`
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
// PIDFD_SELF_THREAD/PIDFD_SELF_THREAD_GROUP won't work with
|
||||||
|
// PIDFD_SIGNAL_PROCESS_GROUP unless the current process is
|
||||||
|
// the process group leader.
|
||||||
|
FN_TEST(pidfd_send_signal_self_process_group)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
int stat;
|
||||||
|
|
||||||
|
pid = TEST_SUCC(fork());
|
||||||
|
if (pid == 0) {
|
||||||
|
setup_test_siginfo(&siginfo, SIGTERM, -666);
|
||||||
|
|
||||||
|
CHECK_WITH(pidfd_send_signal(PIDFD_SELF_THREAD, SIGTERM,
|
||||||
|
&siginfo,
|
||||||
|
PIDFD_SIGNAL_PROCESS_GROUP),
|
||||||
|
_ret == -1 && errno == ESRCH);
|
||||||
|
CHECK_WITH(pidfd_send_signal(PIDFD_SELF_THREAD_GROUP, SIGTERM,
|
||||||
|
&siginfo,
|
||||||
|
PIDFD_SIGNAL_PROCESS_GROUP),
|
||||||
|
_ret == -1 && errno == ESRCH);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_RES(waitpid(pid, &stat, 0),
|
||||||
|
WIFEXITED(stat) && WEXITSTATUS(stat) == 0);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
void *pidfd_send_signal_self_child_thread(void *arg)
|
||||||
|
{
|
||||||
|
setup_test_siginfo(&siginfo, SIGTERM, -666);
|
||||||
|
|
||||||
|
CHECK_WITH(pidfd_send_signal(PIDFD_SELF_THREAD, SIGTERM, &siginfo,
|
||||||
|
PIDFD_SIGNAL_THREAD_GROUP),
|
||||||
|
_ret == 0);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PIDFD_SELF_THREAD will work with PIDFD_SIGNAL_THREAD_GROUP
|
||||||
|
// even if the current thread is not the thread group leader.
|
||||||
|
FN_TEST(pidfd_send_signal_self_thread_group)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
int stat;
|
||||||
|
pthread_t thread;
|
||||||
|
|
||||||
|
pid = TEST_SUCC(fork());
|
||||||
|
if (pid == 0) {
|
||||||
|
CHECK(pthread_create(&thread, NULL,
|
||||||
|
&pidfd_send_signal_self_child_thread,
|
||||||
|
NULL));
|
||||||
|
CHECK(pthread_join(thread, NULL));
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_RES(waitpid(pid, &stat, 0),
|
||||||
|
WIFSIGNALED(stat) && WTERMSIG(stat) == SIGTERM);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
void *pidfd_send_signal_self_process_group_child_thread(void *arg)
|
||||||
|
{
|
||||||
|
setup_test_siginfo(&siginfo, SIGTERM, -666);
|
||||||
|
|
||||||
|
CHECK_WITH(pidfd_send_signal(PIDFD_SELF_THREAD, SIGTERM, &siginfo,
|
||||||
|
PIDFD_SIGNAL_PROCESS_GROUP),
|
||||||
|
_ret == -1 && errno == ESRCH);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PIDFD_SELF_THREAD won't work with PIDFD_SIGNAL_PROCESS_GROUP
|
||||||
|
// unless the current process is the process group leader and
|
||||||
|
// the current thread is the main thread.
|
||||||
|
FN_TEST(pidfd_send_signal_self_process_group_non_main_thread)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
int stat;
|
||||||
|
pthread_t thread;
|
||||||
|
|
||||||
|
pid = TEST_SUCC(fork());
|
||||||
|
if (pid == 0) {
|
||||||
|
CHECK(setpgid(0, 0));
|
||||||
|
|
||||||
|
CHECK(pthread_create(
|
||||||
|
&thread, NULL,
|
||||||
|
pidfd_send_signal_self_process_group_child_thread,
|
||||||
|
NULL));
|
||||||
|
CHECK(pthread_join(thread, NULL));
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_RES(waitpid(pid, &stat, 0),
|
||||||
|
WIFEXITED(stat) && WEXITSTATUS(stat) == 0);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
// FIXME: Enable thread tests once pidfd for threads is supported
|
||||||
|
#ifndef __asterinas__
|
||||||
|
/* ==========================
|
||||||
|
* Tests for threads
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
volatile sig_atomic_t signal_received = 0;
|
||||||
|
|
||||||
|
pthread_t thread;
|
||||||
|
volatile pid_t thread_tid;
|
||||||
|
|
||||||
|
int proc_fd;
|
||||||
|
int proc_task_fd;
|
||||||
|
|
||||||
|
void signal_handler(int signo)
|
||||||
|
{
|
||||||
|
signal_received = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *thread_func(void *arg)
|
||||||
|
{
|
||||||
|
thread_tid = syscall(SYS_gettid);
|
||||||
|
signal(sig, signal_handler);
|
||||||
|
|
||||||
|
while (!signal_received) {
|
||||||
|
usleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
FN_SETUP(create_thread)
|
||||||
|
{
|
||||||
|
static char path[256];
|
||||||
|
|
||||||
|
CHECK(pthread_create(&thread, NULL, thread_func, NULL));
|
||||||
|
|
||||||
|
while (thread_tid == 0) {
|
||||||
|
usleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "/proc/%d/", thread_tid);
|
||||||
|
proc_fd = CHECK(open(path, O_DIRECTORY | O_CLOEXEC));
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "/proc/%d/task/%d", getpid(), thread_tid);
|
||||||
|
proc_task_fd = CHECK(open(path, O_DIRECTORY | O_CLOEXEC));
|
||||||
|
}
|
||||||
|
END_SETUP()
|
||||||
|
|
||||||
|
FN_TEST(pidfd_send_signal_thread)
|
||||||
|
{
|
||||||
|
TEST_ERRNO(pidfd_send_signal(proc_task_fd, sig, &siginfo, 0), EBADF);
|
||||||
|
|
||||||
|
TEST_SUCC(pidfd_send_signal(proc_fd, sig, &siginfo, 0));
|
||||||
|
TEST_SUCC(pthread_join(thread, NULL));
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
|
FN_SETUP(cleanup_thread)
|
||||||
|
{
|
||||||
|
CHECK(close(proc_fd));
|
||||||
|
CHECK(close(proc_task_fd));
|
||||||
|
}
|
||||||
|
END_SETUP()
|
||||||
|
#endif
|
||||||
Loading…
Reference in New Issue