Support CLONE_PARENT flag

This commit is contained in:
jiangjianfeng 2025-09-22 08:02:43 +00:00 committed by Ruihan Li
parent 899db770f6
commit 702aa7d9ee
15 changed files with 335 additions and 94 deletions

View File

@ -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<Process>,
pub thread_local: &'a ThreadLocal,
pub posix_thread: &'a PosixThread,
pub thread: &'a Thread,

View File

@ -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
// <https://www.man7.org/linux/man-pages/man2/clone.2.html>.
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<u64> 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<Tid> {
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<Arc<Task>> {
let clone_flags = clone_args.flags;
// This combination is not valid, according to the Linux man pages. See
// <https://www.man7.org/linux/man-pages/man2/clone.2.html>.
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<Process>,
executable_path: &str,
process_vm: ProcessVm,
resource_limits: ResourceLimits,
@ -689,7 +697,6 @@ fn create_child_process(
) -> Arc<Process> {
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<Process>) {
// 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<Process>, child: &Arc<Process>) {
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<Process>) -> Cow<'_, Arc<Process>> {
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)
}
}

View File

@ -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 <https://man7.org/linux/man-pages/man2/pr_set_pdeathsig.2const.html>.
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(())
}

View File

@ -32,7 +32,7 @@ pub fn kill(pid: Pid, signal: Option<UserSignal>, 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

View File

@ -48,7 +48,6 @@ fn create_init_process(
envp: Vec<CString>,
) -> Result<Arc<Process>> {
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,

View File

@ -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<dyn Terminal>)
.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<BTreeMap<Pid, Arc<Process>>>,
children: Mutex<Option<BTreeMap<Pid, Arc<Process>>>>,
/// Process group
pub(super) process_group: Mutex<Weak<ProcessGroup>>,
/// 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<Process>,
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<BTreeMap<Pid, Arc<Process>>> {
pub(super) fn children(&self) -> &Mutex<Option<BTreeMap<Pid, Arc<Process>>>> {
&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());
}

View File

@ -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);

View File

@ -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(),
);
}

View File

@ -23,7 +23,7 @@ pub fn sys_clone(
) -> Result<SyscallReturn> {
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<Clone3Args> for CloneArgs {
fn from(value: Clone3Args) -> Self {
impl TryFrom<Clone3Args> for CloneArgs {
type Error = Error;
fn try_from(value: Clone3Args) -> Result<Self> {
// 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<Clone3Args> 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),
}
})
}
}

View File

@ -24,7 +24,7 @@ pub fn sys_getrusage(target: i32, rusage_addr: Vaddr, ctx: &Context) -> Result<S
if rusage_addr != 0 {
let rusage = match rusage_target {
RusageTarget::ForSelf => {
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(),

View File

@ -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))?;
}

View File

@ -7,7 +7,7 @@ use crate::{
};
pub fn sys_setpgid(pid: Pid, pgid: Pgid, ctx: &Context) -> Result<SyscallReturn> {
let current = ctx.process;
let current = ctx.process.as_ref();
// The documentation quoted below is from
// <https://www.man7.org/linux/man-pages/man2/setpgid.2.html>.

View File

@ -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);
}
}

View File

@ -0,0 +1,168 @@
// SPDX-License-Identifier: MPL-2.0
#define _GNU_SOURCE
#include <linux/sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sched.h>
#include <fcntl.h>
#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()

View File

@ -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