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