diff --git a/kernel/src/context.rs b/kernel/src/context.rs index 0d9bdeb5f..6a8d65142 100644 --- a/kernel/src/context.rs +++ b/kernel/src/context.rs @@ -23,7 +23,7 @@ use crate::{ /// The context that can be accessed from the current POSIX thread. #[derive(Clone)] pub struct Context<'a> { - pub process: &'a Process, + pub process: Arc, pub thread_local: &'a ThreadLocal, pub posix_thread: &'a PosixThread, pub thread: &'a Thread, diff --git a/kernel/src/process/clone.rs b/kernel/src/process/clone.rs index 719018112..cfe2e62f6 100644 --- a/kernel/src/process/clone.rs +++ b/kernel/src/process/clone.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 +use alloc::borrow::Cow; use core::{num::NonZeroU64, sync::atomic::Ordering}; use aster_util::per_cpu_counter::PerCpuCounter; @@ -180,6 +181,49 @@ impl CloneArgs { ..Default::default() } } + + pub(self) fn check(&self, ctx: &Context) -> Result<()> { + let clone_flags = self.flags; + clone_flags.check_unsupported_flags()?; + + // Reject invalid argument combinations related to the CLONE_PARENT flag. + if clone_flags.contains(CloneFlags::CLONE_PARENT) { + if clone_flags.intersects(CloneFlags::CLONE_NEWUSER | CloneFlags::CLONE_NEWPID) { + return_errno_with_message!( + Errno::EINVAL, + "`CLONE_PARENT` cannot be used together with `CLONE_NEWUSER` or `CLONE_NEWPID`" + ); + } + + if ctx.process.is_init_process() { + return_errno_with_message!( + Errno::EINVAL, + "`CLONE_PARENT` cannot be used if the process is the init process" + ) + } + } + + // Reject invalid argument combinations related to the CLONE_THREAD flag. + if self.flags.contains(CloneFlags::CLONE_THREAD) { + // This combination is not valid, according to the Linux man pages. See + // . + if !clone_flags.contains(CloneFlags::CLONE_VM | CloneFlags::CLONE_SIGHAND) { + return_errno_with_message!( + Errno::EINVAL, + "`CLONE_THREAD` without `CLONE_VM` and `CLONE_SIGHAND` is not valid" + ); + } + + if clone_flags.intersects(CloneFlags::CLONE_PIDFD | CloneFlags::CLONE_NEWUSER) { + return_errno_with_message!( + Errno::EINVAL, + "`CLONE_THREAD` cannot be used together with `CLONE_PIDFD` or `CLONE_NEWUSER`" + ); + } + } + + Ok(()) + } } impl From for CloneFlags { @@ -204,7 +248,8 @@ impl CloneFlags { | CloneFlags::CLONE_CHILD_SETTID | CloneFlags::CLONE_CHILD_CLEARTID | CloneFlags::CLONE_VFORK - | CloneFlags::CLONE_NEWNS; + | CloneFlags::CLONE_NEWNS + | CloneFlags::CLONE_PARENT; let unsupported_flags = *self - supported_flags; if !unsupported_flags.is_empty() { warn!("contains unsupported clone flags: {:?}", unsupported_flags); @@ -222,7 +267,8 @@ pub fn clone_child( parent_context: &UserContext, clone_args: CloneArgs, ) -> Result { - clone_args.flags.check_unsupported_flags()?; + clone_args.check(ctx)?; + if clone_args.flags.contains(CloneFlags::CLONE_THREAD) { let child_task = clone_child_task(ctx, parent_context, clone_args)?; let child_thread = child_task.as_thread().unwrap(); @@ -240,7 +286,7 @@ pub fn clone_child( if child_process.status().is_vfork_child() { let cond = || (!child_process.status().is_vfork_child()).then_some(()); - let current = ctx.process; + let current = ctx.process.as_ref(); current.children_wait_queue().wait_until(cond); } @@ -260,29 +306,6 @@ fn clone_child_task( ) -> Result> { let clone_flags = clone_args.flags; - // This combination is not valid, according to the Linux man pages. See - // . - if !clone_flags.contains(CloneFlags::CLONE_VM | CloneFlags::CLONE_SIGHAND) { - return_errno_with_message!( - Errno::EINVAL, - "`CLONE_THREAD` without `CLONE_VM` and `CLONE_SIGHAND` is not valid" - ); - } - - if clone_flags.contains(CloneFlags::CLONE_PIDFD) { - return_errno_with_message!( - Errno::EINVAL, - "`CLONE_THREAD` cannot be used together with `CLONE_PIDFD`" - ); - } - - if clone_flags.contains(CloneFlags::CLONE_NEWUSER) { - return_errno_with_message!( - Errno::EINVAL, - "`CLONE_THREAD` cannot be used together with `CLONE_NEWUSER`" - ) - } - let Context { process, thread_local, @@ -469,7 +492,6 @@ fn clone_child_process( create_child_process( child_tid, - posix_thread.weak_process(), &child_elf_path, child_process_vm, child_resource_limits, @@ -488,14 +510,7 @@ fn clone_child_process( }; // Sets parent process and group for child process. - set_parent_and_group(process, &child); - - // Updates `has_child_subreaper` for the child process after inserting - // it to its parent's children to make sure the `has_child_subreaper` - // state of the child process will be consistent with its parent. - if process.has_child_subreaper.load(Ordering::Relaxed) { - child.has_child_subreaper.store(true, Ordering::Relaxed); - } + set_parent_and_group(clone_flags, process, &child); Ok(child) } @@ -558,12 +573,6 @@ fn clone_user_ctx( // The return value of child thread is zero child_context.set_syscall_ret(0); - if clone_flags.contains(CloneFlags::CLONE_VM) && !clone_flags.contains(CloneFlags::CLONE_VFORK) - { - // If parent and child shares the same address space and not in vfork situation, - // a new stack must be specified. - debug_assert!(new_sp != 0); - } if new_sp != 0 { // If stack size is not 0, the `new_sp` points to the BOTTOMMOST byte of stack. if let Some(size) = stack_size { @@ -677,7 +686,6 @@ fn clone_ns_proxy( #[expect(clippy::too_many_arguments)] fn create_child_process( pid: Pid, - parent: Weak, executable_path: &str, process_vm: ProcessVm, resource_limits: ResourceLimits, @@ -689,7 +697,6 @@ fn create_child_process( ) -> Arc { let child_proc = Process::new( pid, - parent, executable_path.to_string(), process_vm, resource_limits, @@ -705,25 +712,60 @@ fn create_child_process( child_proc } -fn set_parent_and_group(parent: &Process, child: &Arc) { - // Lock order: children of process -> process table -> group of process - // -> group inner -> session inner - let mut children_mut = parent.children().lock(); +fn set_parent_and_group(clone_flags: CloneFlags, parent: &Arc, child: &Arc) { + loop { + let real_parent = clone_parent(clone_flags, parent); - let mut process_table_mut = process_table::process_table_mut(); + // Lock the parent's children before checking its status. + let mut children_mut = real_parent.children().lock(); - let process_group_mut = parent.process_group.lock(); + let Some(children_mut) = children_mut.as_mut() else { + // The real parent is concurrently exiting group. + // The children have been cleared, + // so the retrial will see an up-to-date real parent. + continue; + }; - let process_group = process_group_mut.upgrade().unwrap(); - let mut process_group_inner = process_group.lock(); + // Lock order: children of process -> parent of process + child.parent().lock().set_process(&real_parent); - // Put the child process in the parent's process group - process_group_inner.insert_process(child.clone()); - *child.process_group.lock() = Arc::downgrade(&process_group); + // Update `has_child_subreaper` for the child process to + // make sure the `has_child_subreaper` state of the child process + // will be consistent with its parent. + if real_parent.has_child_subreaper.load(Ordering::Relaxed) { + child.has_child_subreaper.store(true, Ordering::Relaxed); + } - // Put the child process in the parent's `children` field - children_mut.insert(child.pid(), child.clone()); + // Lock order: children of process -> process table + // -> group of process -> group inner - // Put the child process in the global table - process_table_mut.insert(child.pid(), child.clone()); + let mut process_table_mut = process_table::process_table_mut(); + + let process_group_mut = parent.process_group.lock(); + + let process_group = process_group_mut.upgrade().unwrap(); + let mut process_group_inner = process_group.lock(); + + // Put the child process in the parent's process group + process_group_inner.insert_process(child.clone()); + *child.process_group.lock() = Arc::downgrade(&process_group); + + // Put the child process in the parent's `children` field + children_mut.insert(child.pid(), child.clone()); + + // Put the child process in the global table + process_table_mut.insert(child.pid(), child.clone()); + + return; + } +} + +fn clone_parent(clone_flags: CloneFlags, current: &Arc) -> Cow<'_, Arc> { + if clone_flags.contains(CloneFlags::CLONE_PARENT) { + // The parent process of the current process cannot be `None`, since we have checked that + // the current process is not the init process. + Cow::Owned(current.parent().lock().process().upgrade().unwrap()) + } else { + Cow::Borrowed(current) + } } diff --git a/kernel/src/process/exit.rs b/kernel/src/process/exit.rs index 5a446b2b6..cb315b69c 100644 --- a/kernel/src/process/exit.rs +++ b/kernel/src/process/exit.rs @@ -34,7 +34,8 @@ pub(super) fn exit_process(current_process: &Process) { // that created the child exits, not when the whole process exits. For more details, see the // "CAVEATS" section in . fn send_parent_death_signal(current_process: &Process) { - for (_, child) in current_process.children().lock().iter() { + let current_children = current_process.children().lock(); + for child in current_children.as_ref().unwrap().values() { let Some(signum) = child.parent_death_signal() else { continue; }; @@ -82,17 +83,24 @@ fn move_process_children( // Take the lock first to avoid the race when the `reaper_process` is exiting concurrently. let mut reaper_process_children = reaper_process.children().lock(); - let is_init = is_init_process(reaper_process); - let is_zombie = reaper_process.status().is_zombie(); - if !is_init && is_zombie { + let Some(reaper_process_children) = reaper_process_children.as_mut() else { + // The reaper process has exited, and it is not the init process + // (since we never clear the init process's children). return Err(()); - } + }; - for (_, child_process) in current_process.children().lock().extract_if(|_, _| true) { + // Lock order: children of process -> parent of process + // We holds the lock of children while update the children's parents. + // This ensures when dealing with CLONE_PARENT, + // the retrial will see an up-to-date real parent. + let mut current_children = current_process.children().lock(); + for child_process in current_children.as_mut().unwrap().values() { let mut parent = child_process.parent.lock(); reaper_process_children.insert(child_process.pid(), child_process.clone()); parent.set_process(reaper_process); } + *current_children = None; + Ok(()) } diff --git a/kernel/src/process/kill.rs b/kernel/src/process/kill.rs index e41743bbc..9fcdc1150 100644 --- a/kernel/src/process/kill.rs +++ b/kernel/src/process/kill.rs @@ -32,7 +32,7 @@ pub fn kill(pid: Pid, signal: Option, ctx: &Context) -> Result<()> { return Ok(()); } - return kill_process(ctx.process, Some(signal), ctx); + return kill_process(ctx.process.as_ref(), Some(signal), ctx); } // Slow path diff --git a/kernel/src/process/process/init_proc.rs b/kernel/src/process/process/init_proc.rs index 1915c77c5..cd137b416 100644 --- a/kernel/src/process/process/init_proc.rs +++ b/kernel/src/process/process/init_proc.rs @@ -48,7 +48,6 @@ fn create_init_process( envp: Vec, ) -> Result> { let pid = allocate_posix_tid(); - let parent = Weak::new(); let process_vm = ProcessVm::alloc(); let resource_limits = ResourceLimits::default(); let nice = Nice::default(); @@ -58,7 +57,6 @@ fn create_init_process( let init_proc = Process::new( pid, - parent, executable_path.to_string(), process_vm, resource_limits, diff --git a/kernel/src/process/process/mod.rs b/kernel/src/process/process/mod.rs index 9ebe70923..d5b3e6188 100644 --- a/kernel/src/process/process/mod.rs +++ b/kernel/src/process/process/mod.rs @@ -62,7 +62,7 @@ pub(super) fn init_on_each_cpu() { pub(super) fn init_in_first_process(ctx: &Context) { // FIXME: This should be done by the userspace init process. (crate::device::tty::system_console().clone() as Arc) - .set_control(ctx.process) + .set_control(ctx.process.as_ref()) .unwrap(); } @@ -86,7 +86,7 @@ pub struct Process { /// Parent process pub(super) parent: ParentProcess, /// Children processes - children: Mutex>>, + children: Mutex>>>, /// Process group pub(super) process_group: Mutex>, /// The resource usage statistics of reaped child processes. @@ -203,7 +203,6 @@ impl Process { #[expect(clippy::too_many_arguments)] pub(super) fn new( pid: Pid, - parent: Weak, executable_path: String, process_vm: ProcessVm, @@ -227,8 +226,8 @@ impl Process { children_wait_queue, pidfile_pollee: Pollee::new(), status: ProcessStatus::default(), - parent: ParentProcess::new(parent), - children: Mutex::new(BTreeMap::new()), + parent: ParentProcess::new(Weak::new()), + children: Mutex::new(Some(BTreeMap::new())), process_group: Mutex::new(Weak::new()), reaped_children_stats: Mutex::new(ReapedChildrenStats::default()), is_child_subreaper: AtomicBool::new(false), @@ -312,7 +311,7 @@ impl Process { self.parent.lock().process().upgrade().is_none() } - pub(super) fn children(&self) -> &Mutex>> { + pub(super) fn children(&self) -> &Mutex>>> { &self.children } @@ -771,7 +770,11 @@ impl Process { fn propagate_has_child_subreaper(&self) { let mut process_queue = VecDeque::new(); let children = self.children().lock(); - for child_process in children.values() { + let Some(children_ref) = children.as_ref() else { + // The current process is exiting group at the same time. + return; + }; + for child_process in children_ref.values() { if !child_process.has_child_subreaper.load(Ordering::Acquire) { process_queue.push_back(child_process.clone()); } @@ -780,7 +783,11 @@ impl Process { while let Some(process) = process_queue.pop_front() { process.has_child_subreaper.store(true, Ordering::Release); let children = process.children().lock(); - for child_process in children.values() { + let Some(children_ref) = children.as_ref() else { + // The process is exiting group at the same time. + continue; + }; + for child_process in children_ref.values() { if !child_process.has_child_subreaper.load(Ordering::Acquire) { process_queue.push_back(child_process.clone()); } diff --git a/kernel/src/process/signal/mod.rs b/kernel/src/process/signal/mod.rs index 8261ab151..be780a858 100644 --- a/kernel/src/process/signal/mod.rs +++ b/kernel/src/process/signal/mod.rs @@ -62,7 +62,7 @@ pub fn handle_pending_signal( }; let posix_thread = ctx.posix_thread; - let current = ctx.process; + let current = ctx.process.as_ref(); let signal = { let sig_mask = posix_thread.sig_mask().load(Ordering::Relaxed); diff --git a/kernel/src/process/wait.rs b/kernel/src/process/wait.rs index 9b2f997b2..d7aa0649f 100644 --- a/kernel/src/process/wait.rs +++ b/kernel/src/process/wait.rs @@ -81,8 +81,9 @@ pub fn do_wait( // We want to ensure that multiple waiting threads // do not return the same waited process status. let mut children_lock = ctx.process.children().lock(); + let children_mut = children_lock.as_mut().unwrap(); - let unwaited_children = children_lock + let unwaited_children = children_mut .values() .filter(|child| match &child_filter { ProcessFilter::Any => true, @@ -105,7 +106,7 @@ pub fn do_wait( if !wait_options.contains(WaitOptions::WNOWAIT) { reap_zombie_child( status.pid(), - &mut children_lock, + children_mut, ctx.process.reaped_children_stats(), ); } diff --git a/kernel/src/syscall/clone.rs b/kernel/src/syscall/clone.rs index 16753510f..c3079c980 100644 --- a/kernel/src/syscall/clone.rs +++ b/kernel/src/syscall/clone.rs @@ -23,7 +23,7 @@ pub fn sys_clone( ) -> Result { let args = CloneArgs::for_clone(clone_flags, parent_tidptr, child_tidptr, tls, new_sp)?; debug!("flags = {:?}, child_stack_ptr = 0x{:x}, parent_tid_ptr = 0x{:x?}, child tid ptr = 0x{:x}, tls = 0x{:x}", args.flags, args.stack, args.parent_tid, args.child_tid, args.tls); - let child_pid = clone_child(ctx, parent_context, args).unwrap(); + let child_pid = clone_child(ctx, parent_context, args)?; Ok(SyscallReturn::Return(child_pid as _)) } @@ -45,7 +45,7 @@ pub fn sys_clone3( let clone_args = { let args: Clone3Args = ctx.user_space().read_val(clong_args_addr)?; trace!("clone3 args = {:x?}", args); - CloneArgs::from(args) + CloneArgs::try_from(args)? }; debug!("clone args = {:x?}", clone_args); @@ -81,8 +81,10 @@ struct Clone3Args { cgroup: u64, } -impl From for CloneArgs { - fn from(value: Clone3Args) -> Self { +impl TryFrom for CloneArgs { + type Error = Error; + + fn try_from(value: Clone3Args) -> Result { // TODO: Deal with set_tid, set_tid_size, cgroup if value.set_tid != 0 || value.set_tid_size != 0 { warn!("set_tid is not supported"); @@ -92,18 +94,32 @@ impl From for CloneArgs { warn!("cgroup is not supported"); } - Self { - flags: CloneFlags::from_bits_truncate(value.flags as u32), + let flags = CloneFlags::from_bits(value.flags as u32) + .ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid clone flags"))?; + let exit_signal = + (value.exit_signal != 0).then(|| SigNum::from_u8(value.exit_signal as u8)); + + if flags.intersects(CloneFlags::CLONE_PARENT | CloneFlags::CLONE_THREAD) + && exit_signal.is_some() + { + return_errno_with_message!( + Errno::EINVAL, + "`CLONE_PARENT` and `CLONE_THREAD` cannot be used if the exit signal is specified" + ) + } + + Ok(Self { + flags, pidfd: Some(value.pidfd as Vaddr), child_tid: value.child_tid as _, parent_tid: Some(value.parent_tid as _), - exit_signal: (value.exit_signal != 0).then(|| SigNum::from_u8(value.exit_signal as u8)), + exit_signal, stack: value.stack, stack_size: NonZeroU64::new(value.stack_size), tls: value.tls, _set_tid: Some(value.set_tid), _set_tid_size: Some(value.set_tid_size), _cgroup: Some(value.cgroup), - } + }) } } diff --git a/kernel/src/syscall/getrusage.rs b/kernel/src/syscall/getrusage.rs index d3bdf1979..54522857f 100644 --- a/kernel/src/syscall/getrusage.rs +++ b/kernel/src/syscall/getrusage.rs @@ -24,7 +24,7 @@ pub fn sys_getrusage(target: i32, rusage_addr: Vaddr, ctx: &Context) -> Result { - let process = ctx.process; + let process = ctx.process.as_ref(); rusage_t { ru_utime: process.prof_clock().user_clock().read_time().into(), ru_stime: process.prof_clock().kernel_clock().read_time().into(), diff --git a/kernel/src/syscall/prctl.rs b/kernel/src/syscall/prctl.rs index e6587d5b2..18003a505 100644 --- a/kernel/src/syscall/prctl.rs +++ b/kernel/src/syscall/prctl.rs @@ -81,7 +81,7 @@ pub fn sys_prctl( } } PrctlCmd::PR_SET_CHILD_SUBREAPER(is_set) => { - let process = ctx.process; + let process = ctx.process.as_ref(); if is_set { process.set_child_subreaper(); } else { @@ -89,7 +89,7 @@ pub fn sys_prctl( } } PrctlCmd::PR_GET_CHILD_SUBREAPER(write_addr) => { - let process = ctx.process; + let process = ctx.process.as_ref(); ctx.user_space() .write_val(write_addr, &(process.is_child_subreaper() as u32))?; } diff --git a/kernel/src/syscall/setpgid.rs b/kernel/src/syscall/setpgid.rs index 821c0ab05..2175eb9bc 100644 --- a/kernel/src/syscall/setpgid.rs +++ b/kernel/src/syscall/setpgid.rs @@ -7,7 +7,7 @@ use crate::{ }; pub fn sys_setpgid(pid: Pid, pgid: Pgid, ctx: &Context) -> Result { - let current = ctx.process; + let current = ctx.process.as_ref(); // The documentation quoted below is from // . diff --git a/kernel/src/thread/task.rs b/kernel/src/thread/task.rs index 72e64a5b6..e9ad1e6b3 100644 --- a/kernel/src/thread/task.rs +++ b/kernel/src/thread/task.rs @@ -64,7 +64,7 @@ pub fn create_new_user_task( let has_kernel_event_fn = || current_posix_thread.has_pending(); let ctx = Context { - process: current_process.as_ref(), + process: current_process, thread_local: current_thread_local, posix_thread: current_posix_thread, thread: current_thread.as_ref(), @@ -111,8 +111,8 @@ pub fn create_new_user_task( // Certain signals, such as SIGKILL, should be handled even if the process is stopped. // We need to further investigate Linux behavior regarding which signals should be handled // when the thread is stopped. - while !current_thread.is_exited() && current_process.is_stopped() { - let _ = stop_waiter.pause_until(|| (!current_process.is_stopped()).then_some(())); + while !current_thread.is_exited() && ctx.process.is_stopped() { + let _ = stop_waiter.pause_until(|| (!ctx.process.is_stopped()).then_some(())); handle_pending_signal(user_ctx, &ctx, None); } } diff --git a/test/src/apps/clone3/clone_parent.c b/test/src/apps/clone3/clone_parent.c new file mode 100644 index 000000000..b3990b9de --- /dev/null +++ b/test/src/apps/clone3/clone_parent.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: MPL-2.0 + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../test.h" + +pid_t sys_clone3(struct clone_args *args) +{ + return syscall(SYS_clone3, args, sizeof(struct clone_args)); +} + +FN_TEST(clone_process) +{ + pid_t pid = getpid(); + TEST_ERRNO(wait4(-1, NULL, 0, NULL), ECHILD); + + pid_t child_pid = TEST_SUCC(fork()); + + if (child_pid == 0) { + // Child process + CHECK_WITH(getppid(), _ret == pid); + + // CLONE_PARENT cannot be used if exit_signal is not 0 + struct clone_args args = { .flags = CLONE_PARENT, + .exit_signal = SIGCHLD }; + CHECK_WITH(sys_clone3(&args), errno == EINVAL); + + args.exit_signal = 0; + + // Check process group and session + pid_t sid = CHECK(setsid()); + pid_t pgid = CHECK(getpgrp()); + pid_t grandchild_pid = CHECK(sys_clone3(&args)); + if (grandchild_pid == 0) { + // Grandchild process 1 + CHECK_WITH(getppid(), _ret == pid); + CHECK_WITH(getpgrp(), _ret == pgid); + CHECK_WITH(getsid(0), _ret == sid); + exit(EXIT_SUCCESS); + } + + // Check files + int pipefds[2]; + CHECK(pipe(pipefds)); + grandchild_pid = CHECK(sys_clone3(&args)); + if (grandchild_pid == 0) { + // Grandchild process 2 + CHECK(close(pipefds[0])); + char buf[1] = { 'a' }; + CHECK_WITH(write(pipefds[1], buf, 1), _ret == 1); + exit(EXIT_SUCCESS); + } + + CHECK(close(pipefds[1])); + char buf[1]; + CHECK_WITH(read(pipefds[0], buf, 1), + _ret == 1 && buf[0] == 'a'); + + // Check child subreaper + CHECK(prctl(PR_SET_CHILD_SUBREAPER, 1)); + int subreaper = 0; + CHECK_WITH(prctl(PR_GET_CHILD_SUBREAPER, &subreaper), + subreaper == 1); + grandchild_pid = CHECK(sys_clone3(&args)); + if (grandchild_pid == 0) { + // Grandchild process 3 + CHECK_WITH(prctl(PR_GET_CHILD_SUBREAPER, &subreaper), + subreaper == 0); + exit(EXIT_SUCCESS); + } + + CHECK_WITH(wait4(-1, NULL, 0, NULL), errno == ECHILD); + exit(EXIT_SUCCESS); + } + + // Parent process + int status = 0; + TEST_RES(wait4(-1, &status, 0, NULL), + WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS); + TEST_RES(wait4(-1, &status, 0, NULL), + WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS); + TEST_RES(wait4(-1, &status, 0, NULL), + WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS); + TEST_RES(wait4(-1, &status, 0, NULL), + WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS); + TEST_ERRNO(wait4(-1, NULL, 0, NULL), ECHILD); +} +END_TEST() + +int pipefds[2]; +// A stack for the new thread +#define STACK_SIZE (1024 * 1024) // 1MB stack +char child_stack[STACK_SIZE]; + +int child_function(void *arg) +{ + char buf[1] = { 'a' }; + CHECK(write(pipefds[1], buf, 1)); + return 0; +} + +FN_TEST(clone_thread) +{ + char buf[1] = { 0 }; + TEST_SUCC((pipe(pipefds))); + + // Clone + TEST_SUCC(clone(child_function, child_stack + STACK_SIZE, + CLONE_PARENT | CLONE_THREAD | CLONE_VM | CLONE_SIGHAND, + NULL)); + TEST_RES(read(pipefds[0], buf, 1), buf[0] == 'a'); + + // Clone + TEST_SUCC(clone(child_function, child_stack + STACK_SIZE, + CLONE_PARENT | CLONE_THREAD | CLONE_VM | CLONE_SIGHAND | + SIGCHLD, + NULL)); + TEST_RES(read(pipefds[0], buf, 1), buf[0] == 'a'); + + // Clone3 + struct clone_args args = { .flags = CLONE_PARENT | CLONE_THREAD | + CLONE_VM | CLONE_SIGHAND, + .exit_signal = SIGCHLD }; + TEST_ERRNO(sys_clone3(&args), EINVAL); + + // Clone3 + args.flags = CLONE_THREAD | CLONE_VM | CLONE_SIGHAND; + TEST_ERRNO(sys_clone3(&args), EINVAL); +} +END_TEST() + +// The init process in each PID namespaces can not specify the CLONE_PARENT flags. +// The whole test case is commented out since Asterinas does not support PID namespace. +// FN_TEST(clone_init_process) +// { +// struct clone_args args = { .flags = CLONE_NEWPID }; +// +// int child_pid = TEST_SUCC(sys_clone3(&args)); +// +// if (child_pid == 0) { +// // Child process +// CHECK_WITH(getpid(), _ret == 1); +// +// args.flags = CLONE_PARENT; +// CHECK_WITH(sys_clone3(&args), errno == EINVAL); +// +// args.flags = CLONE_PARENT | CLONE_THREAD | CLONE_VM | +// CLONE_SIGHAND; +// CHECK_WITH(sys_clone3(&args), errno == EINVAL); +// +// exit(EXIT_SUCCESS); +// } +// +// int status = 0; +// TEST_RES(wait4(-1, &status, 0, NULL), +// _ret == child_pid && WIFEXITED(status) && +// WEXITSTATUS(status) == EXIT_SUCCESS); +// TEST_ERRNO(wait4(-1, NULL, 0, NULL), ECHILD); +// } +// END_TEST() \ No newline at end of file diff --git a/test/src/apps/scripts/process.sh b/test/src/apps/scripts/process.sh index 408a799b2..7f6b75a5a 100755 --- a/test/src/apps/scripts/process.sh +++ b/test/src/apps/scripts/process.sh @@ -13,6 +13,7 @@ tests=" clone3/clone_exit_signal clone3/clone_files clone3/clone_no_exit_signal +clone3/clone_parent clone3/clone_process cpu_affinity/cpu_affinity execve/execve