Unshare signal handler during execve

This commit is contained in:
jiangjianfeng 2025-09-29 07:54:35 +00:00 committed by Ruihan Li
parent 207bfe30e2
commit caeec3cdc1
7 changed files with 44 additions and 26 deletions

View File

@ -626,15 +626,17 @@ fn clone_files(parent_file_table: &RwArc<FileTable>, clone_flags: CloneFlags) ->
}
fn clone_sighand(
parent_sig_dispositions: &Arc<Mutex<SigDispositions>>,
parent_sig_dispositions: &Mutex<Arc<Mutex<SigDispositions>>>,
clone_flags: CloneFlags,
) -> Arc<Mutex<SigDispositions>> {
// If CLONE_SIGHAND is set, the child and parent shares the same signal handlers.
// Otherwise, the child has a copy of the parent's signal handlers.
if clone_flags.contains(CloneFlags::CLONE_SIGHAND) {
parent_sig_dispositions.clone()
parent_sig_dispositions.lock().clone()
} else {
Arc::new(Mutex::new(*parent_sig_dispositions.lock()))
let sig_dispositions = parent_sig_dispositions.lock();
let sig_dispositions = sig_dispositions.lock();
Arc::new(Mutex::new(*sig_dispositions))
}
}

View File

@ -161,8 +161,8 @@ fn do_execve_no_return(
*posix_thread.thread_name().lock() = ThreadName::new_from_executable_path(&executable_path);
process.set_executable_path(executable_path);
// Reset signal dispositions to their default actions.
process.sig_dispositions().lock().inherit();
// Unshare and reset signal dispositions to their default actions.
unshare_and_reset_sigdispositions(process);
// Reset the alternate signal stack to its default state.
*thread_local.sig_stack().borrow_mut() = SigStack::default();
// Restore the process exit signal to SIGCHLD.
@ -303,3 +303,12 @@ fn unshare_and_close_files(ctx: &Context) {
.write()
.close_files_on_exec();
}
fn unshare_and_reset_sigdispositions(process: &Process) {
let mut sig_dispositions = process.sig_dispositions().lock();
let mut new = *sig_dispositions.lock();
new.inherit();
*sig_dispositions = Arc::new(Mutex::new(new));
}

View File

@ -132,6 +132,7 @@ pub fn kill_all(signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
fn kill_process(process: &Process, signal: Option<UserSignal>, ctx: &Context) -> Result<()> {
let sig_dispositions = process.sig_dispositions().lock();
let sig_dispositions = sig_dispositions.lock();
let tasks = process.tasks().lock();
let signum = signal.map(|signal| signal.num());
@ -196,7 +197,7 @@ fn kill_process(process: &Process, signal: Option<UserSignal>, ctx: &Context) ->
return Ok(());
}
thread_to_enqueue.enqueue_signal_locked(Box::new(signal), sig_dispositions);
thread_to_enqueue.enqueue_signal_locked(Box::new(signal), &sig_dispositions);
Ok(())
}

View File

@ -248,13 +248,14 @@ impl PosixThread {
pub fn enqueue_signal(&self, signal: Box<dyn Signal>) {
let process = self.process();
let sig_dispositions = process.sig_dispositions().lock();
let sig_dispositions = sig_dispositions.lock();
let signum = signal.num();
if sig_dispositions.get(signum).will_ignore(signum) {
return;
}
self.enqueue_signal_locked(signal, sig_dispositions);
self.enqueue_signal_locked(signal, &sig_dispositions);
}
/// Enqueues a thread-directed signal with locked dispositions.
@ -271,7 +272,7 @@ impl PosixThread {
pub(in crate::process) fn enqueue_signal_locked(
&self,
signal: Box<dyn Signal>,
_sig_dispositions: MutexGuard<SigDispositions>,
_sig_dispositions: &SigDispositions,
) {
self.sig_queues.enqueue(signal);
self.wake_signalled_waker();

View File

@ -118,7 +118,7 @@ pub struct Process {
// Signal
/// Sig dispositions
sig_dispositions: Arc<Mutex<SigDispositions>>,
sig_dispositions: Mutex<Arc<Mutex<SigDispositions>>>,
/// The signal that the process should receive when parent process exits.
parent_death_signal: AtomicSigNum,
@ -232,7 +232,7 @@ impl Process {
reaped_children_stats: Mutex::new(ReapedChildrenStats::default()),
is_child_subreaper: AtomicBool::new(false),
has_child_subreaper: AtomicBool::new(false),
sig_dispositions,
sig_dispositions: Mutex::new(sig_dispositions),
parent_death_signal: AtomicSigNum::new_empty(),
exit_signal: AtomicSigNum::new_empty(),
resource_limits,
@ -628,7 +628,7 @@ impl Process {
// ****************** Signal ******************
pub fn sig_dispositions(&self) -> &Arc<Mutex<SigDispositions>> {
pub fn sig_dispositions(&self) -> &Mutex<Arc<Mutex<SigDispositions>>> {
&self.sig_dispositions
}
@ -647,6 +647,7 @@ impl Process {
}
let sig_dispositions = self.sig_dispositions.lock();
let sig_dispositions = sig_dispositions.lock();
// Drop the signal if it's ignored. See explanation at `enqueue_signal_locked`.
let signum = signal.num();
@ -660,7 +661,7 @@ impl Process {
for thread in threads.as_slice() {
let posix_thread = thread.as_posix_thread().unwrap();
if !posix_thread.has_signal_blocked(signal.num()) {
posix_thread.enqueue_signal_locked(Box::new(signal), sig_dispositions);
posix_thread.enqueue_signal_locked(Box::new(signal), &sig_dispositions);
return;
}
}
@ -668,7 +669,7 @@ impl Process {
// If all threads block the signal, enqueue the signal to the main thread.
let thread = threads.main();
let posix_thread = thread.as_posix_thread().unwrap();
posix_thread.enqueue_signal_locked(Box::new(signal), sig_dispositions);
posix_thread.enqueue_signal_locked(Box::new(signal), &sig_dispositions);
}
/// Clears the parent death signal.

View File

@ -75,9 +75,22 @@ pub fn handle_pending_signal(
let sig_num = signal.num();
trace!("sig_num = {:?}, sig_name = {}", sig_num, sig_num.sig_name());
let mut sig_dispositions = current.sig_dispositions().lock();
let sig_action = {
let sig_dispositions = current.sig_dispositions().lock();
let mut sig_dispositions = sig_dispositions.lock();
let sig_action = sig_dispositions.get(sig_num);
let sig_action = sig_dispositions.get(sig_num);
if let SigAction::User { flags, .. } = &sig_action
&& flags.contains(SigActionFlags::SA_RESETHAND)
{
// In Linux, SA_RESETHAND corresponds to SA_ONESHOT,
// which means the user handler will be executed only once and then reset to the default.
// Refer to https://elixir.bootlin.com/linux/v6.0.9/source/kernel/signal.c#L2761.
sig_dispositions.set_default(sig_num);
}
sig_action
};
trace!("sig action: {:x?}", sig_action);
match sig_action {
@ -105,14 +118,6 @@ pub fn handle_pending_signal(
.set_instruction_pointer(user_ctx.instruction_pointer() - SYSCALL_INSTR_LEN);
}
if flags.contains(SigActionFlags::SA_RESETHAND) {
// In Linux, SA_RESETHAND corresponds to SA_ONESHOT,
// which means the user handler will be executed only once and then reset to the default.
// Refer to https://elixir.bootlin.com/linux/v6.0.9/source/kernel/signal.c#L2761.
sig_dispositions.set_default(sig_num);
}
drop(sig_dispositions);
if let Err(e) = handle_user_signal(
ctx,
sig_num,
@ -130,8 +135,6 @@ pub fn handle_pending_signal(
}
}
SigAction::Dfl => {
drop(sig_dispositions);
let sig_default_action = SigDefaultAction::from_signum(sig_num);
trace!("sig_default_action: {:?}", sig_default_action);
match sig_default_action {

View File

@ -35,7 +35,8 @@ pub fn sys_rt_sigaction(
return_errno_with_message!(Errno::EINVAL, "sigset size is not equal to 8");
}
let mut sig_dispositions = ctx.process.sig_dispositions().lock();
let sig_dispositions = ctx.process.sig_dispositions().lock();
let mut sig_dispositions = sig_dispositions.lock();
let old_action = if sig_action_addr != 0 {
if sig_num == SIGKILL || sig_num == SIGSTOP {