From 0a36760f7a42b7fe27735e29f971423f6ec4b1ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Tue, 24 Sep 2024 21:33:10 +0200 Subject: [PATCH] Respect user-defined exit signal in clone() and clone3() When calling clone() and clone3(), the user is allowed to specify a signal to be sent to the parent process on exit. Respect this value by storing it in the Process struct and sending the signal on exit. Add a test as well to verify that the signal is properly delivered to the parent. --- kernel/src/process/clone.rs | 4 ++ kernel/src/process/exit.rs | 9 ++-- kernel/src/process/process/mod.rs | 12 ++++++ kernel/src/syscall/clone.rs | 12 +----- test/apps/clone3/clone_exit_signal.c | 55 +++++++++++++++++++++++++ test/apps/clone3/clone_no_exit_signal.c | 42 +++++++++++++++++++ test/apps/scripts/process.sh | 2 + 7 files changed, 122 insertions(+), 14 deletions(-) create mode 100644 test/apps/clone3/clone_exit_signal.c create mode 100644 test/apps/clone3/clone_no_exit_signal.c diff --git a/kernel/src/process/clone.rs b/kernel/src/process/clone.rs index a1531d8ef..24f2c4eef 100644 --- a/kernel/src/process/clone.rs +++ b/kernel/src/process/clone.rs @@ -345,6 +345,10 @@ fn clone_child_process( process_builder.build()? }; + if let Some(sig) = clone_args.exit_signal { + child.set_exit_signal(sig); + }; + // Deals with clone flags let child_thread = thread_table::get_thread(child_tid).unwrap(); let child_posix_thread = child_thread.as_posix_thread().unwrap(); diff --git a/kernel/src/process/exit.rs b/kernel/src/process/exit.rs index c0fedd853..8e6330f71 100644 --- a/kernel/src/process/exit.rs +++ b/kernel/src/process/exit.rs @@ -5,7 +5,7 @@ use crate::{ prelude::*, process::{ posix_thread::{do_exit, PosixThreadExt}, - signal::{constants::SIGCHLD, signals::kernel::KernelSignal}, + signal::signals::kernel::KernelSignal, }, thread::Thread, }; @@ -60,10 +60,11 @@ pub fn do_exit_group(term_status: TermStatus) { let parent = current.parent().lock().process(); if let Some(parent) = parent.upgrade() { // Notify parent - let signal = KernelSignal::new(SIGCHLD); - parent.enqueue_signal(signal); + if let Some(signal) = current.exit_signal().map(KernelSignal::new) { + parent.enqueue_signal(signal); + }; parent.children_wait_queue().wake_all(); - } + }; } const INIT_PROCESS_PID: Pid = 1; diff --git a/kernel/src/process/process/mod.rs b/kernel/src/process/process/mod.rs index b6b61749f..8aea28fe6 100644 --- a/kernel/src/process/process/mod.rs +++ b/kernel/src/process/process/mod.rs @@ -101,6 +101,9 @@ pub struct Process { /// The signal that the process should receive when parent process exits. parent_death_signal: AtomicSigNum, + /// The signal that should be sent to the parent when this process exits. + exit_signal: AtomicSigNum, + /// A profiling clock measures the user CPU time and kernel CPU time of the current process. prof_clock: Arc, @@ -218,6 +221,7 @@ impl Process { umask, sig_dispositions, parent_death_signal: AtomicSigNum::new_empty(), + exit_signal: AtomicSigNum::new_empty(), resource_limits: Mutex::new(resource_limits), nice: Atomic::new(nice), timer_manager: PosixTimerManager::new(&prof_clock, process_ref), @@ -690,6 +694,14 @@ impl Process { self.parent_death_signal.as_sig_num() } + pub fn set_exit_signal(&self, sig_num: SigNum) { + self.exit_signal.set(sig_num); + } + + pub fn exit_signal(&self) -> Option { + self.exit_signal.as_sig_num() + } + // ******************* Status ******************** fn set_runnable(&self) { diff --git a/kernel/src/syscall/clone.rs b/kernel/src/syscall/clone.rs index 01c53c933..cb35d0bcc 100644 --- a/kernel/src/syscall/clone.rs +++ b/kernel/src/syscall/clone.rs @@ -7,11 +7,7 @@ use ostd::cpu::UserContext; use super::SyscallReturn; use crate::{ prelude::*, - process::{ - clone_child, - signal::{constants::SIGCHLD, sig_num::SigNum}, - CloneArgs, CloneFlags, - }, + process::{clone_child, signal::sig_num::SigNum, CloneArgs, CloneFlags}, }; // The order of arguments for clone differs in different architecture. @@ -87,11 +83,7 @@ struct Clone3Args { impl From for CloneArgs { fn from(value: Clone3Args) -> Self { - // TODO: deal with pidfd, exit_signal, set_tid, set_tid_size, cgroup - if value.exit_signal != 0 || value.exit_signal as u8 != SIGCHLD.as_u8() { - warn!("exit signal is not supported"); - } - + // TODO: deal with pidfd, set_tid, set_tid_size, cgroup if value.pidfd != 0 { warn!("pidfd is not supported"); } diff --git a/test/apps/clone3/clone_exit_signal.c b/test/apps/clone3/clone_exit_signal.c new file mode 100644 index 000000000..5c339f9e4 --- /dev/null +++ b/test/apps/clone3/clone_exit_signal.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MPL-2.0 + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +static pid_t sys_clone3(struct clone_args *args) +{ + return syscall(SYS_clone3, args, sizeof(struct clone_args)); +} + +int child_exit_recv = 0; + +void sig_handler(int signal) +{ + printf("Received child exit signal\n"); + child_exit_recv++; +} + +int main(int argc, char *argv[]) +{ + pid_t pid; + struct clone_args args = { + .exit_signal = SIGUSR2, + }; + + signal(SIGUSR2, sig_handler); + + pid = sys_clone3(&args); + if (pid < 0) + err(EXIT_FAILURE, "Failed to create new process"); + + if (pid == 0) { + printf("Child process with pid %d\n", getpid()); + exit(EXIT_SUCCESS); + } + + /* + * From the clone(2) manual: + * If [the exit signal] is specified as anything other than SIGCHLD, + * then the parent process must specify the __WALL or __WCLONE + * options when waiting for the child with wait(2). + */ + waitpid(pid, NULL, __WALL); + + if (child_exit_recv != 1) + errx(EXIT_FAILURE, "did not receive exit signal from child"); + + return 0; +} diff --git a/test/apps/clone3/clone_no_exit_signal.c b/test/apps/clone3/clone_no_exit_signal.c new file mode 100644 index 000000000..720a01a4d --- /dev/null +++ b/test/apps/clone3/clone_no_exit_signal.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MPL-2.0 + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +static pid_t sys_clone3(struct clone_args *args) +{ + return syscall(SYS_clone3, args, sizeof(struct clone_args)); +} + +int main(int argc, char *argv[]) +{ + pid_t pid; + struct clone_args args = { + .exit_signal = 0, + }; + + pid = sys_clone3(&args); + if (pid < 0) + err(EXIT_FAILURE, "Failed to create new process"); + + if (pid == 0) { + printf("Child process with pid %d\n", getpid()); + exit(EXIT_SUCCESS); + } + + /* + * From the clone(2) manual: + * If [the exit signal] is specified as anything other than SIGCHLD, + * then the parent process must specify the __WALL or __WCLONE + * options when waiting for the child with wait(2). + */ + waitpid(pid, NULL, __WALL); + + /* We should have gotten this far without receiving any signals */ + return 0; +} diff --git a/test/apps/scripts/process.sh b/test/apps/scripts/process.sh index 56bc06b57..f08c3c86f 100755 --- a/test/apps/scripts/process.sh +++ b/test/apps/scripts/process.sh @@ -10,6 +10,8 @@ cd ${SCRIPT_DIR}/.. echo "Start process test......" # These test programs are sorted by name. tests=" +clone3/clone_exit_signal +clone3/clone_no_exit_signal clone3/clone_process execve/execve eventfd2/eventfd2