Support CLONE_PARENT flag
This commit is contained in:
parent
899db770f6
commit
702aa7d9ee
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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))?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue