Skip to main content

starnix_core/execution/
task_creation.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::fs::tmpfs::TmpFs;
6use crate::mm::MemoryManager;
7use crate::security;
8use crate::signals::SignalActions;
9use crate::task::{
10    CurrentTask, Kernel, Pid, PidTableGuard, RobustListHeadPtr, SeccompFilterContainer,
11    SeccompState, Task, TaskBuilder, ThreadGroup, ThreadGroupParent, ThreadGroupWriteGuard,
12};
13use crate::vfs::{FsContext, Namespace, SharedFdTable};
14use starnix_sync::allow_subclass;
15use starnix_task_command::TaskCommand;
16use starnix_types::arch::ArchWidth;
17use starnix_types::release_on_error;
18use starnix_uapi::auth::Credentials;
19use starnix_uapi::errors::Errno;
20use starnix_uapi::resource_limits::Resource;
21use starnix_uapi::signals::{SIGCHLD, Signal};
22use starnix_uapi::{errno, error, from_status_like_fdio, rlimit};
23use std::ffi::CString;
24use std::sync::Arc;
25use std::sync::atomic::Ordering;
26
27/// Result returned when creating new Zircon processes for tasks.
28///
29/// This does not include the task's Zircon thread. Backing threads are attached later in the task
30/// lifecycle, when creating an execution context in [`execute_task()`].
31pub struct TaskInfo {
32    /// The thread group that the task should be added to.
33    pub thread_group: Arc<ThreadGroup>,
34
35    /// The memory manager to use for the task.
36    pub memory_manager: Option<Arc<MemoryManager>>,
37}
38
39/// Whether `create_zircon_process()` may create a process while the kernel shuts down.
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum CreateDuringShutdown {
42    /// Never, e.g. when the kernel starts a process.
43    Never,
44    /// Until the deferred shutdown deadline, when a deferred shutdown process creates it. See
45    /// `ThreadGroupMutableState::is_deferred_shutdown_process()`.
46    UntilDeadline,
47}
48
49pub fn create_zircon_process(
50    kernel: &Arc<Kernel>,
51    parent: Option<ThreadGroupWriteGuard<'_>>,
52    pid: Pid,
53    exit_signal: Option<Signal>,
54    signal_actions: Arc<SignalActions>,
55    name: TaskCommand,
56    create_during_shutdown: CreateDuringShutdown,
57) -> Result<TaskInfo, Errno> {
58    // During shutdown, only deferred shutdown processes may create processes, and only until the
59    // deferred shutdown deadline. The processes they create are deferred shutdown processes too.
60    // This keeps Kernel::run_shutdown() finite. See
61    // ThreadGroupMutableState::is_deferred_shutdown_process().
62    let deferred_shutdown_deadline = kernel.deferred_shutdown_deadline();
63    if let Some(deadline) = deferred_shutdown_deadline {
64        if create_during_shutdown == CreateDuringShutdown::Never
65            || zx::MonotonicInstant::get() >= deadline
66        {
67            return error!(EBUSY);
68        }
69    }
70    let (process, root_vmar) =
71        create_shared(&kernel.kthreads.starnix_process, zx::ProcessOptions::empty(), name)
72            .map_err(|status| from_status_like_fdio!(status))?;
73
74    // Make sure that if this process panics in normal mode that the whole kernel's job is killed.
75    fuchsia_runtime::job_default()
76        .set_critical(zx::JobCriticalOptions::RETCODE_NONZERO, &process)
77        .map_err(|status| from_status_like_fdio!(status))?;
78
79    let thread_group = ThreadGroup::new(
80        kernel.clone(),
81        process,
82        root_vmar,
83        parent,
84        pid,
85        exit_signal,
86        signal_actions,
87    );
88    if deferred_shutdown_deadline.is_some() {
89        thread_group.created_during_shutdown.store(true, Ordering::Relaxed);
90    }
91
92    Ok(TaskInfo { thread_group, memory_manager: None })
93}
94
95/// Creates a process that shares half its address space with this process.
96///
97/// The created process will also share its handle table and futex context with `self`.
98///
99/// Returns the created process and a handle to the created process' restricted address space.
100///
101/// Wraps the
102/// [zx_process_create_shared](https://fuchsia.dev/fuchsia-src/reference/syscalls/process_create_shared.md)
103/// syscall.
104fn create_shared(
105    process: &zx::Process,
106    options: zx::ProcessOptions,
107    name: TaskCommand,
108) -> Result<(zx::Process, zx::Vmar), zx::Status> {
109    let self_raw = process.raw_handle();
110    let name_bytes = name.as_bytes();
111    let mut process_out = 0;
112    let mut restricted_vmar_out = 0;
113    #[allow(
114        clippy::undocumented_unsafe_blocks,
115        reason = "Force documented unsafe blocks in Starnix"
116    )]
117    let status = unsafe {
118        zx::sys::zx_process_create_shared(
119            self_raw,
120            options.bits(),
121            name_bytes.as_ptr(),
122            name_bytes.len(),
123            &mut process_out,
124            &mut restricted_vmar_out,
125        )
126    };
127    zx::ok(status)?;
128    #[allow(
129        clippy::undocumented_unsafe_blocks,
130        reason = "Force documented unsafe blocks in Starnix"
131    )]
132    unsafe {
133        Ok((
134            zx::Process::from(zx::NullableHandle::from_raw(process_out)),
135            zx::Vmar::from(zx::NullableHandle::from_raw(restricted_vmar_out)),
136        ))
137    }
138}
139
140/// Create a process that is a child of the `init` process.
141///
142/// The created process will be a task that is the leader of a new thread group.
143///
144/// Most processes are created by userspace and are descendants of the `init` process. In
145/// some situations, the kernel needs to create a process itself. This function is the
146/// preferred way of creating an actual userspace process because making the process a child of
147/// `init` means that `init` is responsible for waiting on the process when it dies and thereby
148/// cleaning up its zombie.
149///
150/// If you just need a kernel task, and not an entire userspace process, consider using the
151/// `kthreads` threadpool instead.
152///
153/// If `seclabel` is set, or the container specified a `default_seclabel`, then it will be
154/// resolved against the `kernel`'s active security policy, and applied to the new task.
155/// Otherwise the task will inherit its LSM state from the "init" task.
156///
157/// This function creates an underlying Zircon process to host the new task.
158pub fn create_init_child_process(
159    kernel: &Arc<Kernel>,
160    initial_name: TaskCommand,
161    mut creds: Credentials,
162    seclabel: Option<&CString>,
163) -> Result<TaskBuilder, Errno> {
164    let init_task = kernel.get_init_task()?;
165
166    let fs = init_task.fs()?.fork();
167
168    let security_state = if let Some(seclabel) = seclabel {
169        security::task_for_context(&init_task, seclabel.as_bytes().into())?
170    } else if let Some(default_seclabel) = kernel.features.default_seclabel.as_ref() {
171        security::task_for_context(&init_task, default_seclabel.as_bytes().into())?
172    } else {
173        // If SELinux is enabled then this call will fail with `EINVAL`.
174        security::task_for_context(&init_task, b"".into()).map_err(|_| {
175            errno!(EINVAL, "Container has SELinux enabled but no Security Context specified")
176        })?
177    };
178    creds.security_state = security_state;
179
180    let task = create_task(
181        kernel,
182        initial_name.clone(),
183        fs,
184        |pid| {
185            create_zircon_process(
186                kernel,
187                None,
188                pid,
189                Some(SIGCHLD),
190                SignalActions::default(),
191                initial_name.clone(),
192                CreateDuringShutdown::Never,
193            )
194        },
195        creds.into(),
196    )?;
197    {
198        let mut init_writer = init_task.thread_group().write();
199        // Init is the parent of every other process, so this matches the lock
200        // ordering from parent to child.
201        let _token = allow_subclass();
202        let mut new_process_writer = task.thread_group().write();
203        new_process_writer.parent =
204            Some(ThreadGroupParent::new(Arc::downgrade(&init_task.thread_group())));
205        init_writer.children.insert(task.pid.clone());
206    }
207    // A child process created via fork(2) inherits its parent's
208    // resource limits.  Resource limits are preserved across execve(2).
209    let limits = init_task.thread_group().limits.lock().clone();
210    *task.thread_group().limits.lock() = limits;
211    Ok(task)
212}
213
214/// Creates the initial process for a kernel.
215///
216/// The created process will be a task that is the leader of a new thread group.
217///
218/// The init process is special because it's the root of the parent/child relationship between
219/// tasks. If a task dies, the init process is ultimately responsible for waiting on that task
220/// and removing it from the zombie list.
221///
222/// It's possible for the kernel to create tasks whose ultimate parent isn't init, but such
223/// tasks cannot be created by userspace directly.
224///
225/// This function should only be called as part of booting a kernel instance. To create a
226/// process after the kernel has already booted, consider `create_init_child_process`, or use
227/// `kthreads` for kernel background work.
228///
229/// The process created by this function should always have pid 1. We require the caller to
230/// pass the `pid` as an argument to clarify that it's the callers responsibility to determine
231/// the pid for the process.
232pub fn create_init_process(
233    kernel: &Arc<Kernel>,
234    pid: Pid,
235    initial_name: TaskCommand,
236    fs: Arc<FsContext>,
237    rlimits: &[(Resource, u64)],
238) -> Result<TaskBuilder, Errno> {
239    assert_eq!(pid.id, 1);
240    let builder = create_task_with_pid(
241        kernel,
242        kernel.pids.lock(),
243        pid,
244        initial_name.clone(),
245        fs,
246        |pid| {
247            create_zircon_process(
248                kernel,
249                None,
250                pid,
251                Some(SIGCHLD),
252                SignalActions::default(),
253                initial_name.clone(),
254                CreateDuringShutdown::Never,
255            )
256        },
257        Credentials::root(),
258        rlimits,
259    )?;
260    let _ = kernel.init_task.set(Arc::downgrade(&builder.task));
261    Ok(builder)
262}
263
264/// Creates the `kthreadd` system task that runs inside the kernel.
265///
266/// There is no underlying Zircon process to host the task. Instead, the work done by this task
267/// is performed by a thread in the original Starnix process, possibly as part of a thread
268/// pool.
269///
270/// The task is initialized with a temporary bootstrap `"rootfs"` [`TmpFs`] intended to be
271/// replaced with the container's root filesystem via [`FsContext::replace_bootstrap_namespace`]
272/// during kernel boot.
273///
274/// This function should only be called during kernel initialization to set up `kthreads`. To do
275/// background work inside the kernel after boot, use `kthreads`, which provides both the system
276/// task and a threadpool on which it can do work.
277pub fn create_system_task(kernel: &Arc<Kernel>) -> Result<CurrentTask, Errno> {
278    let fs = FsContext::new(Namespace::new(TmpFs::new_fs_with_name(kernel, "rootfs".into())));
279    let builder = create_task(
280        kernel,
281        TaskCommand::new(b"kthreadd"),
282        fs,
283        |pid| {
284            let thread_group = ThreadGroup::for_system(kernel.clone(), pid);
285            Ok(TaskInfo { thread_group, memory_manager: None }.into())
286        },
287        Credentials::root(),
288    )?;
289    Ok(builder.into())
290}
291
292pub fn create_task<F>(
293    kernel: &Kernel,
294    initial_name: TaskCommand,
295    root_fs: Arc<FsContext>,
296    task_info_factory: F,
297    creds: Arc<Credentials>,
298) -> Result<TaskBuilder, Errno>
299where
300    F: FnOnce(Pid) -> Result<TaskInfo, Errno>,
301{
302    let mut guard = kernel.pids.lock();
303    let pid = guard.allocate_pid()?;
304    create_task_with_pid(kernel, guard, pid, initial_name, root_fs, task_info_factory, creds, &[])
305}
306
307fn create_task_with_pid<F>(
308    kernel: &Kernel,
309    mut pids: PidTableGuard<'_>,
310    pid: Pid,
311    initial_name: TaskCommand,
312    root_fs: Arc<FsContext>,
313    task_info_factory: F,
314    creds: Arc<Credentials>,
315    rlimits: &[(Resource, u64)],
316) -> Result<TaskBuilder, Errno>
317where
318    F: FnOnce(Pid) -> Result<TaskInfo, Errno>,
319{
320    debug_assert!(pid.get_task().is_err());
321
322    let TaskInfo { thread_group, memory_manager } = task_info_factory(pid.clone())?;
323
324    // > The timer slack values of init (PID 1), the ancestor of all processes, are 50,000
325    // > nanoseconds (50 microseconds).  The timer slack value is inherited by a child created
326    // > via fork(2), and is preserved across execve(2).
327    // https://man7.org/linux/man-pages/man2/prctl.2.html
328    let default_timerslack = 50_000;
329    let builder = TaskBuilder {
330        task: Task::new(
331            pid,
332            initial_name,
333            thread_group,
334            SharedFdTable::default(),
335            memory_manager,
336            root_fs,
337            creds,
338            Arc::clone(&kernel.default_abstract_socket_namespace),
339            Arc::clone(&kernel.default_abstract_vsock_namespace),
340            Default::default(),
341            Default::default(),
342            None,
343            Default::default(),
344            kernel.root_uts_ns.clone(),
345            false,
346            SeccompState::default(),
347            SeccompFilterContainer::default(),
348            RobustListHeadPtr::null(&ArchWidth::Arch64),
349            default_timerslack,
350        ),
351        thread_state: Default::default(),
352    };
353    release_on_error!(builder, {
354        builder.thread_group().add(Arc::clone(&builder.task))?;
355        for (resource, limit) in rlimits {
356            builder
357                .thread_group()
358                .limits
359                .lock()
360                .set(*resource, rlimit { rlim_cur: *limit, rlim_max: *limit });
361        }
362
363        let sid = builder.thread_group().read().session.clone();
364        pids.add_task(Arc::clone(&builder.task));
365        pids.attach_pgid(&builder.thread_group().pgid(), builder.thread_group());
366        pids.attach_sid(&sid, builder.thread_group());
367        Ok(())
368    });
369    Ok(builder)
370}
371
372/// Create a kernel task in the same ThreadGroup as the given `system_task`.
373///
374/// There is no underlying Zircon thread to host the task.
375pub fn create_kernel_thread(
376    system_task: &Task,
377    initial_name: TaskCommand,
378) -> Result<CurrentTask, Errno> {
379    let mut pids = system_task.kernel().pids.lock();
380    let pid = pids.allocate_pid()?;
381
382    let scheduler_state = system_task.scheduler_state.lock().fork();
383    let uts_ns;
384    let default_timerslack_ns;
385    {
386        let state = system_task.read();
387        uts_ns = state.uts_ns.clone();
388        default_timerslack_ns = state.default_timerslack_ns;
389    }
390
391    let mm;
392    let fs;
393    let abstract_socket_namespace;
394    let abstract_vsock_namespace;
395    {
396        let running_state = system_task.running_state()?;
397        mm = running_state.mm.upgrade();
398        fs = running_state.fs()?;
399        abstract_socket_namespace = running_state.abstract_socket_namespace.clone();
400        abstract_vsock_namespace = running_state.abstract_vsock_namespace.clone();
401    }
402
403    let current_task: CurrentTask = TaskBuilder::new(Task::new(
404        pid,
405        initial_name,
406        system_task.thread_group().clone(),
407        SharedFdTable::default(),
408        mm,
409        fs,
410        system_task.clone_creds(),
411        abstract_socket_namespace,
412        abstract_vsock_namespace,
413        Default::default(),
414        Default::default(),
415        None,
416        scheduler_state,
417        uts_ns,
418        false,
419        SeccompState::default(),
420        SeccompFilterContainer::default(),
421        RobustListHeadPtr::null(&ArchWidth::Arch64),
422        default_timerslack_ns,
423    ))
424    .into();
425    release_on_error!(current_task, {
426        current_task.thread_group().add(Arc::clone(&current_task.task))?;
427        pids.add_task(Arc::clone(&current_task.task));
428        Ok(())
429    });
430    Ok(current_task)
431}
432
433#[cfg(test)]
434mod test {
435    use super::*;
436    use crate::testing::*;
437    use fuchsia_rcu::RcuReadScope;
438
439    #[::fuchsia::test]
440    async fn test_create_task_failure_does_not_add_process_group() {
441        spawn_kernel_and_run(async |current_task| {
442            let kernel = current_task.kernel();
443            let (kept_pid_sender, kept_pid_receiver) = std::sync::mpsc::channel();
444            let result = super::create_task(
445                kernel,
446                TaskCommand::new(b"failed_task"),
447                current_task.fs(),
448                |pid| {
449                    let _ = kept_pid_sender.send(pid);
450                    error!(EINVAL)
451                },
452                Credentials::root(),
453            );
454            assert!(result.is_err());
455            let kept_pid = kept_pid_receiver.recv().unwrap();
456            assert!(!kept_pid.is_process_group(&RcuReadScope::new()));
457        })
458        .await;
459    }
460}