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