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