starnix_core/task/
kernel.rs

1// Copyright 2021 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::bpf::EbpfState;
6use crate::device::remote_block_device::RemoteBlockDeviceRegistry;
7use crate::device::{DeviceMode, DeviceRegistry};
8use crate::execution::CrashReporter;
9use crate::mm::{FutexTable, MappingSummary, MlockPinFlavor, SharedFutexKey};
10use crate::power::SuspendResumeManagerHandle;
11use crate::ptrace::StopState;
12use crate::security::{self, AuditLogger};
13use crate::task::container_namespace::ContainerNamespace;
14use crate::task::limits::SystemLimits;
15use crate::task::memory_attribution::MemoryAttributionManager;
16use crate::task::net::NetstackDevices;
17use crate::task::tracing::PidToKoidMap;
18use crate::task::{
19    AbstractUnixSocketNamespace, AbstractVsockSocketNamespace, CurrentTask, DelayedReleaser,
20    IpTables, KernelCgroups, KernelStats, KernelThreads, PidTable, SchedulerManager, Syslog,
21    ThreadGroup, UtsNamespace, UtsNamespaceHandle,
22};
23use crate::time::{HrTimerManager, HrTimerManagerHandle};
24use crate::vdso::vdso_loader::Vdso;
25use crate::vfs::fs_args::MountParams;
26use crate::vfs::socket::{
27    GenericMessage, GenericNetlink, NetlinkAccessControl, NetlinkContextImpl,
28    NetlinkToClientSender, SocketAddress, SocketTokensStore,
29};
30use crate::vfs::{CacheConfig, FileOps, FsNodeHandle, FsString, Mounts, NamespaceNode};
31use bstr::{BString, ByteSlice};
32use devicetree::types::Devicetree;
33use expando::Expando;
34use fidl::endpoints::{
35    ClientEnd, ControlHandle, DiscoverableProtocolMarker, ProtocolMarker, create_endpoints,
36};
37use fidl_fuchsia_component_runner::{ComponentControllerControlHandle, ComponentStopInfo};
38use fidl_fuchsia_feedback::CrashReporterProxy;
39use fidl_fuchsia_time_external::AdjustSynchronousProxy;
40use fuchsia_inspect::ArrayProperty;
41use futures::FutureExt;
42use netlink::interfaces::InterfacesHandler;
43use netlink::{NETLINK_LOG_TAG, Netlink};
44use once_cell::sync::OnceCell;
45use starnix_lifecycle::{AtomicU32Counter, AtomicU64Counter};
46use starnix_logging::{SyscallLogFilter, log_debug, log_error, log_info, log_warn};
47use starnix_sync::{
48    FileOpsCore, KernelSwapFiles, LockEqualOrBefore, Locked, Mutex, OrderedMutex, RwLock,
49};
50use starnix_types::ownership::TempRef;
51use starnix_uapi::device_type::DeviceType;
52use starnix_uapi::errors::{Errno, errno};
53use starnix_uapi::open_flags::OpenFlags;
54use starnix_uapi::{VMADDR_CID_HOST, from_status_like_fdio};
55use std::borrow::Cow;
56use std::collections::{HashMap, HashSet};
57use std::num::NonZeroU64;
58use std::path::PathBuf;
59use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU16, Ordering};
60use std::sync::{Arc, OnceLock, Weak};
61use zx::CpuFeatureFlags;
62use {
63    fidl_fuchsia_io as fio, fidl_fuchsia_memory_attribution as fattribution,
64    fuchsia_async as fasync,
65};
66
67#[derive(Debug, Default, Clone)]
68pub struct KernelFeatures {
69    pub bpf_v2: bool,
70
71    /// Whether the kernel supports the S_ISUID and S_ISGID bits.
72    ///
73    /// For example, these bits are used by `sudo`.
74    ///
75    /// Enabling this feature is potentially a security risk because they allow privilege
76    /// escalation.
77    pub enable_suid: bool,
78
79    /// Whether io_uring is enabled.
80    ///
81    /// TODO(https://fxbug.dev/297431387): Enabled by default once the feature is completed.
82    pub io_uring: bool,
83
84    /// Whether the kernel should return an error to userspace, rather than panicking, if `reboot()`
85    /// is requested but cannot be enacted because the kernel lacks the relevant capabilities.
86    pub error_on_failed_reboot: bool,
87
88    /// The default seclabel that is applied to components that are run in this kernel.
89    ///
90    /// Components can override this by setting the `seclabel` field in their program block.
91    pub default_seclabel: Option<String>,
92
93    /// Whether the kernel is being used to run the SELinux Test Suite.
94    ///
95    /// TODO: https://fxbug.dev/388077431 - remove this once we no longer need workarounds for the
96    /// SELinux Test Suite.
97    pub selinux_test_suite: bool,
98
99    /// The default mount options to use when mounting directories from a component's namespace.
100    ///
101    /// The key is the path in the component's namespace, and the value is the mount options
102    /// string.
103    pub default_ns_mount_options: Option<HashMap<String, String>>,
104
105    /// The default uid that is applied to components that are run in this kernel.
106    ///
107    /// Components can override this by setting the `uid` field in their program block.
108    pub default_uid: u32,
109
110    /// mlock() never prefaults pages.
111    pub mlock_always_onfault: bool,
112
113    /// Implementation of mlock() to use for this kernel instance.
114    pub mlock_pin_flavor: MlockPinFlavor,
115
116    /// Whether excessive crash reports should be throttled.
117    pub crash_report_throttling: bool,
118
119    /// Whether or not to serve wifi support to Android.
120    pub wifi: bool,
121
122    /// The number of bytes to cache in pages for reading zx::MapInfo from VMARs.
123    pub cached_zx_map_info_bytes: u32,
124
125    /// The size of the Dirent LRU cache.
126    pub dirent_cache_size: u32,
127}
128
129impl KernelFeatures {
130    /// Returns the `MountParams` to use when mounting the specified path from a component's
131    /// namespace.  This mechanism is also used to specified options for mounts created via
132    /// container features, by specifying a pseudo-path e.g. "#container".
133    pub fn ns_mount_options(&self, ns_path: &str) -> Result<MountParams, Errno> {
134        if let Some(all_options) = &self.default_ns_mount_options {
135            if let Some(options) = all_options.get(ns_path) {
136                return MountParams::parse(options.as_bytes().into());
137            }
138        }
139        Ok(MountParams::default())
140    }
141}
142
143/// Kernel command line argument structure
144pub struct ArgNameAndValue<'a> {
145    pub name: &'a str,
146    pub value: Option<&'a str>,
147}
148
149/// The shared, mutable state for the entire Starnix kernel.
150///
151/// The `Kernel` object holds all kernel threads, userspace tasks, and file system resources for a
152/// single instance of the Starnix kernel. In production, there is one instance of this object for
153/// the entire Starnix kernel. However, multiple instances of this object can be created in one
154/// process during unit testing.
155///
156/// The structure of this object will likely need to evolve as we implement more namespacing and
157/// isolation mechanisms, such as `namespaces(7)` and `pid_namespaces(7)`.
158pub struct Kernel {
159    /// Weak reference to self. Allows to not have to pass &Arc<Kernel> in apis.
160    pub weak_self: Weak<Kernel>,
161
162    /// The kernel threads running on behalf of this kernel.
163    pub kthreads: KernelThreads,
164
165    /// The features enabled for this kernel.
166    pub features: KernelFeatures,
167
168    /// The processes and threads running in this kernel, organized by pid_t.
169    pub pids: RwLock<PidTable>,
170
171    /// Used to record the pid/tid to Koid mappings. Set when collecting trace data.
172    pub pid_to_koid_mapping: Arc<RwLock<Option<PidToKoidMap>>>,
173
174    /// Subsystem-specific properties that hang off the Kernel object.
175    ///
176    /// Instead of adding yet another property to the Kernel object, consider storing the property
177    /// in an expando if that property is only used by one part of the system, such as a module.
178    pub expando: Expando,
179
180    /// The default namespace for abstract AF_UNIX sockets in this kernel.
181    ///
182    /// Rather than use this default namespace, abstract socket addresses
183    /// should be looked up in the AbstractSocketNamespace on each Task
184    /// object because some Task objects might have a non-default namespace.
185    pub default_abstract_socket_namespace: Arc<AbstractUnixSocketNamespace>,
186
187    /// The default namespace for abstract AF_VSOCK sockets in this kernel.
188    pub default_abstract_vsock_namespace: Arc<AbstractVsockSocketNamespace>,
189
190    /// The kernel command line. Shows up in /proc/cmdline.
191    pub cmdline: BString,
192
193    pub device_tree: Option<Devicetree>,
194
195    // Global state held by the Linux Security Modules subsystem.
196    pub security_state: security::KernelState,
197
198    /// The registry of device drivers.
199    pub device_registry: DeviceRegistry,
200
201    /// Mapping of top-level namespace entries to an associated proxy.
202    /// For example, "/svc" to the respective proxy. Only the namespace entries
203    /// which were known at component startup will be available by the kernel.
204    pub container_namespace: ContainerNamespace,
205
206    /// The registry of block devices backed by a remote fuchsia.io file.
207    pub remote_block_device_registry: Arc<RemoteBlockDeviceRegistry>,
208
209    /// The iptables used for filtering network packets.
210    iptables: OnceLock<IpTables>,
211
212    /// The futexes shared across processes.
213    pub shared_futexes: Arc<FutexTable<SharedFutexKey>>,
214
215    /// The default UTS namespace for all tasks.
216    ///
217    /// Because each task can have its own UTS namespace, you probably want to use
218    /// the UTS namespace handle of the task, which may/may not point to this one.
219    pub root_uts_ns: UtsNamespaceHandle,
220
221    /// A struct containing a VMO with a vDSO implementation, if implemented for a given architecture, and possibly an offset for a sigreturn function.
222    pub vdso: Vdso,
223
224    /// A struct containing a VMO with a arch32-vDSO implementation, if implemented for a given architecture.
225    // TODO(https://fxbug.dev/380431743) This could be made less clunky -- maybe a Vec<Vdso> above or
226    // something else
227    pub vdso_arch32: Option<Vdso>,
228
229    /// The table of devices installed on the netstack and their associated
230    /// state local to this `Kernel`.
231    pub netstack_devices: Arc<NetstackDevices>,
232
233    /// Files that are currently available for swapping.
234    /// Note: Starnix never actually swaps memory to these files. We just need to track them
235    /// to pass conformance tests.
236    pub swap_files: OrderedMutex<Vec<FsNodeHandle>, KernelSwapFiles>,
237
238    /// The implementation of generic Netlink protocol families.
239    generic_netlink: OnceLock<GenericNetlink<NetlinkToClientSender<GenericMessage>>>,
240
241    /// The implementation of networking-related Netlink protocol families.
242    network_netlink: OnceLock<Netlink<NetlinkContextImpl>>,
243
244    /// Inspect instrumentation for this kernel instance.
245    pub inspect_node: fuchsia_inspect::Node,
246
247    /// The kinds of seccomp action that gets logged, stored as a bit vector.
248    /// Each potential SeccompAction gets a bit in the vector, as specified by
249    /// SeccompAction::logged_bit_offset.  If the bit is set, that means the
250    /// action should be logged when it is taken, subject to the caveats
251    /// described in seccomp(2).  The value of the bit vector is exposed to users
252    /// in a text form in the file /proc/sys/kernel/seccomp/actions_logged.
253    pub actions_logged: AtomicU16,
254
255    /// The manager for suspend/resume.
256    pub suspend_resume_manager: SuspendResumeManagerHandle,
257
258    /// Unique IDs for new mounts and mount namespaces.
259    pub next_mount_id: AtomicU64Counter,
260    pub next_peer_group_id: AtomicU64Counter,
261    pub next_namespace_id: AtomicU64Counter,
262
263    /// Unique IDs for file objects.
264    pub next_file_object_id: AtomicU64Counter,
265
266    /// Unique cookie used to link two inotify events, usually an IN_MOVE_FROM/IN_MOVE_TO pair.
267    pub next_inotify_cookie: AtomicU32Counter,
268
269    /// Controls which processes a process is allowed to ptrace.  See Documentation/security/Yama.txt
270    pub ptrace_scope: AtomicU8,
271
272    // The Fuchsia build version returned by `fuchsia.buildinfo.Provider`.
273    pub build_version: OnceCell<String>,
274
275    pub stats: Arc<KernelStats>,
276
277    /// Resource limits that are exposed, for example, via sysctl.
278    pub system_limits: SystemLimits,
279
280    // The service to handle delayed releases. This is required for elements that requires to
281    // execute some code when released and requires a known context (both in term of lock context,
282    // as well as `CurrentTask`).
283    pub delayed_releaser: DelayedReleaser,
284
285    /// Manages task priorities.
286    pub scheduler: SchedulerManager,
287
288    /// The syslog manager.
289    pub syslog: Syslog,
290
291    /// All mounts.
292    pub mounts: Mounts,
293
294    /// The manager for creating and managing high-resolution timers.
295    pub hrtimer_manager: HrTimerManagerHandle,
296
297    /// The manager for monitoring and reporting resources used by the kernel.
298    pub memory_attribution_manager: MemoryAttributionManager,
299
300    /// Handler for crashing Linux processes.
301    pub crash_reporter: CrashReporter,
302
303    /// Whether this kernel is shutting down. When shutting down, new processes may not be spawned.
304    shutting_down: AtomicBool,
305
306    /// True to disable syslog access to unprivileged callers.  This also controls whether read
307    /// access to /dev/kmsg requires privileged capabilities.
308    pub restrict_dmesg: AtomicBool,
309
310    /// Determines whether unprivileged BPF is permitted, or can be re-enabled.
311    ///   0 - Unprivileged BPF is permitted.
312    ///   1 - Unprivileged BPF is not permitted, and cannot be enabled.
313    ///   2 - Unprivileged BPF is not permitted, but can be enabled by a privileged task.
314    pub disable_unprivileged_bpf: AtomicU8,
315
316    /// Control handle to the running container's ComponentController.
317    pub container_control_handle: Mutex<Option<ComponentControllerControlHandle>>,
318
319    /// eBPF state: loaded programs, eBPF maps, etc.
320    pub ebpf_state: EbpfState,
321
322    /// Cgroups of the kernel.
323    pub cgroups: KernelCgroups,
324
325    /// Used to communicate requests to adjust system time from within a Starnix
326    /// container. Used from syscalls.
327    pub time_adjustment_proxy: Option<AdjustSynchronousProxy>,
328
329    /// Used to store tokens for sockets, particularly per-uid sharing domain sockets.
330    pub socket_tokens_store: SocketTokensStore,
331
332    /// Hardware capabilities to push onto stack when loading an ELF binary.
333    pub hwcaps: HwCaps,
334
335    /// Filters for syscall logging. Processes with names matching these filters will have syscalls
336    /// logged at INFO level.
337    pub syscall_log_filters: Mutex<Vec<SyscallLogFilter>>,
338}
339
340/// Hardware capabilities.
341#[derive(Debug, Clone, Copy, Default)]
342pub struct HwCap {
343    /// The value for `AT_HWCAP`.
344    pub hwcap: u32,
345    /// The value for `AT_HWCAP2`.
346    pub hwcap2: u32,
347}
348
349/// Hardware capabilities for both 32-bit and 64-bit ELF binaries.
350#[derive(Debug, Clone, Copy, Default)]
351pub struct HwCaps {
352    /// For 32-bit binaries.
353    #[cfg(target_arch = "aarch64")]
354    pub arch32: HwCap,
355    /// For 64-bit binaries.
356    pub arch64: HwCap,
357}
358
359/// An implementation of [`InterfacesHandler`].
360///
361/// This holds a `Weak<Kernel>` because it is held within a [`Netlink`] which
362/// is itself held within an `Arc<Kernel>`. Holding an `Arc<T>` within an
363/// `Arc<T>` prevents the `Arc`'s ref count from ever reaching 0, causing a
364/// leak.
365struct InterfacesHandlerImpl(Weak<Kernel>);
366
367impl InterfacesHandlerImpl {
368    fn kernel(&self) -> Option<Arc<Kernel>> {
369        self.0.upgrade()
370    }
371}
372
373impl InterfacesHandler for InterfacesHandlerImpl {
374    fn handle_new_link(&mut self, name: &str, interface_id: NonZeroU64) {
375        if let Some(kernel) = self.kernel() {
376            kernel.netstack_devices.add_device(&kernel, name.into(), interface_id);
377        }
378    }
379
380    fn handle_deleted_link(&mut self, name: &str) {
381        if let Some(kernel) = self.kernel() {
382            kernel.netstack_devices.remove_device(&kernel, name.into());
383        }
384    }
385
386    fn handle_idle_event(&mut self) {
387        let Some(kernel) = self.kernel() else {
388            log_error!("kernel went away while netlink is initializing");
389            return;
390        };
391        let (initialized, wq) = &kernel.netstack_devices.initialized_and_wq;
392        if initialized.swap(true, Ordering::SeqCst) {
393            log_error!("netlink initial devices should only be reported once");
394            return;
395        }
396        wq.notify_all()
397    }
398}
399
400impl Kernel {
401    pub fn new(
402        cmdline: BString,
403        features: KernelFeatures,
404        system_limits: SystemLimits,
405        container_namespace: ContainerNamespace,
406        scheduler: SchedulerManager,
407        crash_reporter_proxy: Option<CrashReporterProxy>,
408        inspect_node: fuchsia_inspect::Node,
409        security_state: security::KernelState,
410        time_adjustment_proxy: Option<AdjustSynchronousProxy>,
411        device_tree: Option<Devicetree>,
412    ) -> Result<Arc<Kernel>, zx::Status> {
413        let unix_address_maker =
414            Box::new(|x: FsString| -> SocketAddress { SocketAddress::Unix(x) });
415        let vsock_address_maker = Box::new(|x: u32| -> SocketAddress {
416            SocketAddress::Vsock { port: x, cid: VMADDR_CID_HOST }
417        });
418
419        let crash_reporter = CrashReporter::new(
420            &inspect_node,
421            crash_reporter_proxy,
422            zx::Duration::from_minutes(8),
423            features.crash_report_throttling,
424        );
425        let hrtimer_manager = HrTimerManager::new(&inspect_node);
426
427        let cpu_feature_flags =
428            zx::system_get_feature_flags::<CpuFeatureFlags>().unwrap_or_else(|e| {
429                log_debug!("CPU feature flags are only supported on ARM64: {}, reporting 0", e);
430                CpuFeatureFlags::empty()
431            });
432        let hwcaps = HwCaps::from_cpu_feature_flags(cpu_feature_flags);
433
434        let this = Arc::new_cyclic(|kernel| Kernel {
435            weak_self: kernel.clone(),
436            kthreads: KernelThreads::new(kernel.clone()),
437            features,
438            pids: Default::default(),
439            pid_to_koid_mapping: Arc::new(RwLock::new(None)),
440            expando: Default::default(),
441            default_abstract_socket_namespace: AbstractUnixSocketNamespace::new(unix_address_maker),
442            default_abstract_vsock_namespace: AbstractVsockSocketNamespace::new(
443                vsock_address_maker,
444            ),
445            cmdline,
446            device_tree,
447            security_state,
448            device_registry: Default::default(),
449            container_namespace,
450            remote_block_device_registry: Default::default(),
451            iptables: OnceLock::new(),
452            shared_futexes: Arc::<FutexTable<SharedFutexKey>>::default(),
453            root_uts_ns: Arc::new(RwLock::new(UtsNamespace::default())),
454            vdso: Vdso::new(),
455            vdso_arch32: Vdso::new_arch32(),
456            netstack_devices: Arc::default(),
457            swap_files: Default::default(),
458            generic_netlink: OnceLock::new(),
459            network_netlink: OnceLock::new(),
460            inspect_node,
461            actions_logged: AtomicU16::new(0),
462            suspend_resume_manager: Default::default(),
463            next_mount_id: AtomicU64Counter::new(1),
464            next_peer_group_id: AtomicU64Counter::new(1),
465            next_namespace_id: AtomicU64Counter::new(1),
466            next_inotify_cookie: AtomicU32Counter::new(1),
467            next_file_object_id: Default::default(),
468            system_limits,
469            ptrace_scope: AtomicU8::new(0), // Disable YAMA checks by default.
470            restrict_dmesg: AtomicBool::new(false),
471            disable_unprivileged_bpf: AtomicU8::new(0), // Enable unprivileged BPF by default.
472            build_version: OnceCell::new(),
473            stats: Arc::new(KernelStats::default()),
474            delayed_releaser: Default::default(),
475            scheduler,
476            syslog: Default::default(),
477            mounts: Mounts::new(),
478            hrtimer_manager,
479            memory_attribution_manager: MemoryAttributionManager::new(kernel.clone()),
480            crash_reporter,
481            shutting_down: AtomicBool::new(false),
482            container_control_handle: Mutex::new(None),
483            ebpf_state: Default::default(),
484            cgroups: Default::default(),
485            time_adjustment_proxy,
486            socket_tokens_store: Default::default(),
487            hwcaps,
488            syscall_log_filters: Default::default(),
489        });
490
491        // Initialize the device registry before registering any devices.
492        //
493        // We will create sysfs recursively within this function.
494        this.device_registry.objects.init(&mut this.kthreads.unlocked_for_async(), &this);
495
496        // Make a copy of this Arc for the inspect lazy node to use but don't create an Arc cycle
497        // because the inspect node that owns this reference is owned by the kernel.
498        let kernel = Arc::downgrade(&this);
499        this.inspect_node.record_lazy_child("thread_groups", move || {
500            if let Some(kernel) = kernel.upgrade() {
501                let inspector = kernel.get_thread_groups_inspect();
502                async move { Ok(inspector) }.boxed()
503            } else {
504                async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
505            }
506        });
507
508        let kernel = Arc::downgrade(&this);
509        this.inspect_node.record_lazy_child("cgroupv2", move || {
510            if let Some(kernel) = kernel.upgrade() {
511                async move { Ok(kernel.cgroups.cgroup2.get_cgroup_inspect()) }.boxed()
512            } else {
513                async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
514            }
515        });
516
517        Ok(this)
518    }
519
520    /// Shuts down userspace and the kernel in an orderly fashion, eventually terminating the root
521    /// kernel process.
522    pub fn shut_down(self: &Arc<Self>) {
523        // Run shutdown code on a kthread in the main process so that it can be the last process
524        // alive.
525        self.kthreads.spawn_future(
526            {
527                let kernel = self.clone();
528                move || async move {
529                    kernel.run_shutdown().await;
530                }
531            },
532            "run_shutdown",
533        );
534    }
535
536    /// Starts shutting down the Starnix kernel and any running container. Only one thread can drive
537    /// shutdown at a time. This function will return immediately if shut down is already under way.
538    ///
539    /// Shutdown happens in several phases:
540    ///
541    /// 1. Disable launching new processes
542    /// 2. Shut down individual ThreadGroups until only the init and system tasks remain
543    /// 3. Repeat the above for the init task
544    /// 4. Clean up kernel-internal structures that can hold processes alive
545    /// 5. Ensure this process is the only one running in the kernel job.
546    /// 6. Unmounts the kernel's mounts' FileSystems.
547    /// 7. Tell CF the container component has stopped
548    /// 8. Exit this process
549    ///
550    /// If a ThreadGroup does not shut down on its own (including after SIGKILL), that phase of
551    /// shutdown will hang. To gracefully shut down any further we need the other kernel processes
552    /// to do controlled exits that properly release access to shared state. If our orderly shutdown
553    /// does hang, eventually CF will kill the container component which will lead to the job of
554    /// this process being killed and shutdown will still complete.
555    async fn run_shutdown(&self) {
556        const INIT_PID: i32 = 1;
557        const SYSTEM_TASK_PID: i32 = 2;
558
559        // Step 1: Prevent new processes from being created once they observe this update. We don't
560        // want the thread driving shutdown to be racing with other threads creating new processes.
561        if self
562            .shutting_down
563            .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
564            .is_err()
565        {
566            log_debug!("Additional thread tried to initiate shutdown while already in-progress.");
567            return;
568        }
569
570        log_info!("Shutting down Starnix kernel.");
571
572        // Step 2: Shut down thread groups in a loop until init and the system task are all that
573        // remain.
574        loop {
575            let tgs = {
576                // Exiting thread groups need to acquire a write lock for the pid table to
577                // successfully exit so we need to acquire that lock in a reduced scope.
578                self.pids
579                    .read()
580                    .get_thread_groups()
581                    .filter(|tg| tg.leader != SYSTEM_TASK_PID && tg.leader != INIT_PID)
582                    .collect::<Vec<_>>()
583            };
584            if tgs.is_empty() {
585                log_debug!("pid table is empty except init and system task");
586                break;
587            }
588
589            log_debug!(tgs:?; "shutting down thread groups");
590            let mut tasks = vec![];
591            for tg in tgs {
592                let task = fasync::Task::local(ThreadGroup::shut_down(Arc::downgrade(&tg)));
593                tasks.push(task);
594            }
595            futures::future::join_all(tasks).await;
596        }
597
598        // Step 3: Terminate the init process.
599        let maybe_init = {
600            // Exiting thread groups need to acquire a write lock for the pid table to successfully
601            // exit so we need to acquire that lock in a reduced scope.
602            self.pids.read().get_thread_group(1).map(|tg| Arc::downgrade(&tg))
603        };
604        if let Some(init) = maybe_init {
605            log_debug!("shutting down init");
606            ThreadGroup::shut_down(init).await;
607        } else {
608            log_debug!("init already terminated");
609        }
610
611        // Step 4: Clean up any structures that can keep non-Linux processes live in our job.
612        log_debug!("cleaning up pinned memory");
613        self.expando.remove::<memory_pinning::ShadowProcess>();
614
615        // Step 5: Make sure this is the only process running in the job. We already should have
616        // cleared up all processes other than the system task at this point, but wait on any that
617        // might be around for good measure.
618        //
619        // Use unwrap liberally since we're shutting down anyway and errors will still tear down the
620        // kernel.
621        let kernel_job = fuchsia_runtime::job_default();
622        assert_eq!(kernel_job.children().unwrap(), &[], "starnix does not create any child jobs");
623        let own_koid = fuchsia_runtime::process_self().koid().unwrap();
624
625        log_debug!("waiting for this to be the only process in the job");
626        loop {
627            let mut remaining_processes = kernel_job
628                .processes()
629                .unwrap()
630                .into_iter()
631                // Don't wait for ourselves to exit.
632                .filter(|pid| pid != &own_koid)
633                .peekable();
634            if remaining_processes.peek().is_none() {
635                log_debug!("No stray Zircon processes.");
636                break;
637            }
638
639            let mut terminated_signals = vec![];
640            for pid in remaining_processes {
641                let handle = match kernel_job
642                    .get_child(&pid, zx::Rights::BASIC | zx::Rights::PROPERTY | zx::Rights::DESTROY)
643                {
644                    Ok(h) => h,
645                    Err(e) => {
646                        log_debug!(pid:?, e:?; "failed to get child process from job");
647                        continue;
648                    }
649                };
650                log_debug!(
651                    pid:?,
652                    name:? = handle.get_name();
653                    "waiting on process terminated signal"
654                );
655                terminated_signals
656                    .push(fuchsia_async::OnSignals::new(handle, zx::Signals::PROCESS_TERMINATED));
657            }
658            log_debug!("waiting on process terminated signals");
659            futures::future::join_all(terminated_signals).await;
660        }
661
662        // Step 6: Forcibly unmounts the mounts' FileSystems.
663        log_debug!("clearing mounts");
664        self.mounts.clear();
665
666        // Step 7: Tell CF the container stopped.
667        log_debug!("all non-root processes killed, notifying CF container is stopped");
668        if let Some(control_handle) = self.container_control_handle.lock().take() {
669            log_debug!("Notifying CF that the container has stopped.");
670            control_handle
671                .send_on_stop(ComponentStopInfo {
672                    termination_status: Some(zx::Status::OK.into_raw()),
673                    exit_code: Some(0),
674                    ..ComponentStopInfo::default()
675                })
676                .unwrap();
677            control_handle.shutdown_with_epitaph(zx::Status::OK);
678        } else {
679            log_warn!("Shutdown invoked without a container controller control handle.");
680        }
681
682        // Step 8: exiting this process.
683        log_info!("All tasks killed, exiting Starnix kernel root process.");
684        // Normally a Rust program exits its process by calling `std::process::exit()` which goes
685        // through libc to exit the program. This runs drop impls on any thread-local variables
686        // which can cause issues during Starnix shutdown when we haven't yet integrated every
687        // subsystem with the shutdown flow. While those issues are indicative of underlying
688        // problems, we can't solve them without finishing the implementation of graceful shutdown.
689        // Instead, ask Zircon to exit our process directly, bypassing any libc atexit handlers.
690        // TODO(https://fxbug.dev/295073633) return from main instead of avoiding atexit handlers
691        zx::Process::exit(0);
692    }
693
694    pub fn is_shutting_down(&self) -> bool {
695        self.shutting_down.load(Ordering::Acquire)
696    }
697
698    /// Opens a device file (driver) identified by `dev`.
699    pub fn open_device<L>(
700        &self,
701        locked: &mut Locked<L>,
702        current_task: &CurrentTask,
703        node: &NamespaceNode,
704        flags: OpenFlags,
705        dev: DeviceType,
706        mode: DeviceMode,
707    ) -> Result<Box<dyn FileOps>, Errno>
708    where
709        L: LockEqualOrBefore<FileOpsCore>,
710    {
711        self.device_registry.open_device(locked, current_task, node, flags, dev, mode)
712    }
713
714    /// Return a reference to the Audit Framework
715    ///
716    /// This function follows the lazy initialization pattern.
717    pub fn audit_logger(&self) -> Arc<AuditLogger> {
718        self.expando.get_or_init(|| AuditLogger::new(self))
719    }
720
721    /// Return a reference to the GenericNetlink implementation.
722    ///
723    /// This function follows the lazy initialization pattern, where the first
724    /// call will instantiate the Generic Netlink server in a separate kthread.
725    pub fn generic_netlink(&self) -> &GenericNetlink<NetlinkToClientSender<GenericMessage>> {
726        self.generic_netlink.get_or_init(|| {
727            let (generic_netlink, worker_params) = GenericNetlink::new();
728            let enable_nl80211 = self.features.wifi;
729            self.kthreads.spawn_future(
730                move || async move {
731                    crate::vfs::socket::run_generic_netlink_worker(worker_params, enable_nl80211)
732                        .await;
733                    log_error!("Generic Netlink future unexpectedly exited");
734                },
735                "generic_netlink_worker",
736            );
737            generic_netlink
738        })
739    }
740
741    /// Return a reference to the [`netlink::Netlink`] implementation.
742    ///
743    /// This function follows the lazy initialization pattern, where the first
744    /// call will instantiate the Netlink implementation.
745    pub fn network_netlink(self: &Arc<Self>) -> &Netlink<NetlinkContextImpl> {
746        self.network_netlink.get_or_init(|| {
747            let (network_netlink, worker_params) =
748                Netlink::new(InterfacesHandlerImpl(self.weak_self.clone()));
749
750            let kernel = self.clone();
751            self.kthreads.spawn_future(
752                move || async move {
753                    netlink::run_netlink_worker(
754                        worker_params,
755                        NetlinkAccessControl::new(kernel.kthreads.system_task()),
756                    )
757                    .await;
758                    log_error!(tag = NETLINK_LOG_TAG; "Netlink async worker unexpectedly exited");
759                },
760                "network_netlink_worker",
761            );
762            network_netlink
763        })
764    }
765
766    pub fn iptables(&self) -> &IpTables {
767        self.iptables.get_or_init(|| IpTables::new())
768    }
769
770    /// Returns a Proxy to the service used by the container at `filename`.
771    #[allow(unused)]
772    pub fn connect_to_named_protocol_at_container_svc<P: ProtocolMarker>(
773        &self,
774        filename: &str,
775    ) -> Result<ClientEnd<P>, Errno> {
776        match self.container_namespace.get_namespace_channel("/svc") {
777            Ok(channel) => {
778                let (client_end, server_end) = create_endpoints::<P>();
779                fdio::service_connect_at(channel.as_ref(), filename, server_end.into_channel())
780                    .map_err(|status| from_status_like_fdio!(status))?;
781                Ok(client_end)
782            }
783            Err(err) => {
784                log_error!("Unable to get /svc namespace channel! {}", err);
785                Err(errno!(ENOENT))
786            }
787        }
788    }
789
790    /// Returns a Proxy to the service `P` used by the container.
791    pub fn connect_to_protocol_at_container_svc<P: DiscoverableProtocolMarker>(
792        &self,
793    ) -> Result<ClientEnd<P>, Errno> {
794        self.connect_to_named_protocol_at_container_svc::<P>(P::PROTOCOL_NAME)
795    }
796
797    pub fn add_syscall_log_filter(&self, name: &str) {
798        let filter = SyscallLogFilter::new(name.to_string());
799        {
800            let mut filters = self.syscall_log_filters.lock();
801            if filters.contains(&filter) {
802                return;
803            }
804            filters.push(filter);
805        }
806        for headers in self.pids.read().get_thread_groups() {
807            headers.sync_syscall_log_level();
808        }
809    }
810
811    pub fn clear_syscall_log_filters(&self) {
812        {
813            let mut filters = self.syscall_log_filters.lock();
814            if filters.is_empty() {
815                return;
816            }
817            filters.clear();
818        }
819        for headers in self.pids.read().get_thread_groups() {
820            headers.sync_syscall_log_level();
821        }
822    }
823
824    fn get_thread_groups_inspect(&self) -> fuchsia_inspect::Inspector {
825        let inspector = fuchsia_inspect::Inspector::default();
826
827        let thread_groups = inspector.root();
828        let mut mm_summary = MappingSummary::default();
829        let mut mms_summarized = HashSet::new();
830
831        // Avoid holding locks for the entire iteration.
832        let all_thread_groups = {
833            let pid_table = self.pids.read();
834            pid_table.get_thread_groups().collect::<Vec<_>>()
835        };
836        for thread_group in all_thread_groups {
837            // Avoid holding the state lock while summarizing.
838            let (ppid, tasks) = {
839                let tg = thread_group.read();
840                (tg.get_ppid() as i64, tg.tasks().map(TempRef::into_static).collect::<Vec<_>>())
841            };
842
843            let tg_node = thread_groups.create_child(format!("{}", thread_group.leader));
844            if let Ok(koid) = &thread_group.process.koid() {
845                tg_node.record_int("koid", koid.raw_koid() as i64);
846            }
847            tg_node.record_int("pid", thread_group.leader as i64);
848            tg_node.record_int("ppid", ppid);
849            tg_node.record_bool("stopped", thread_group.load_stopped() == StopState::GroupStopped);
850
851            let tasks_node = tg_node.create_child("tasks");
852            for task in tasks {
853                if let Ok(mm) = task.mm() {
854                    if mms_summarized.insert(Arc::as_ptr(&mm) as usize) {
855                        mm.summarize(&mut mm_summary);
856                    }
857                }
858                let set_properties = |node: &fuchsia_inspect::Node| {
859                    node.record_string("command", task.command().to_string());
860
861                    let scheduler_state = task.read().scheduler_state;
862                    if !scheduler_state.is_default() {
863                        node.record_child("sched", |node| {
864                            node.record_string(
865                                "role_name",
866                                self.scheduler
867                                    .role_name(&task)
868                                    .map(|n| Cow::Borrowed(n))
869                                    .unwrap_or_else(|e| Cow::Owned(e.to_string())),
870                            );
871                            node.record_string("state", format!("{scheduler_state:?}"));
872                        });
873                    }
874                };
875                if task.tid == thread_group.leader {
876                    let mut argv = task.read_argv(256).unwrap_or_default();
877
878                    // Any runtime that overwrites argv is likely to leave a lot of trailing
879                    // nulls, no need to print those in inspect.
880                    argv.retain(|arg| !arg.is_empty());
881
882                    let inspect_argv = tg_node.create_string_array("argv", argv.len());
883                    for (i, arg) in argv.iter().enumerate() {
884                        inspect_argv.set(i, arg.to_string());
885                    }
886                    tg_node.record(inspect_argv);
887
888                    set_properties(&tg_node);
889                } else {
890                    tasks_node.record_child(task.tid.to_string(), |task_node| {
891                        set_properties(task_node);
892                    });
893                };
894            }
895            tg_node.record(tasks_node);
896            thread_groups.record(tg_node);
897        }
898
899        thread_groups.record_child("memory_managers", |node| mm_summary.record(node));
900
901        inspector
902    }
903
904    pub fn new_memory_attribution_observer(
905        &self,
906        control_handle: fattribution::ProviderControlHandle,
907    ) -> attribution_server::Observer {
908        self.memory_attribution_manager.new_observer(control_handle)
909    }
910
911    /// Opens and returns a directory proxy from the container's namespace, at
912    /// the requested path, using the provided flags. This method will open the
913    /// closest existing path from the namespace hierarchy, and then attempt
914    /// initialize an open on the remaining subdirectory path, using the given open_flags.
915    ///
916    /// For example, given the parameter provided is `/path/to/foo/bar` and there
917    /// are namespace entries already for `/path/to/foo` and `/path/to`. The entry
918    /// for /path/to/foo will be opened, and then the /bar will attempt to be opened
919    /// underneath that directory with the given open_flags. The returned value
920    /// will be the proxy to the parent (/path/to/foo) and the string to the child
921    /// path (/bar). The caller of this method can expect /bar to be initialized.
922    pub fn open_ns_dir(
923        &self,
924        path: &str,
925        open_flags: fio::Flags,
926    ) -> Result<(fio::DirectorySynchronousProxy, String), Errno> {
927        let ns_path = PathBuf::from(path);
928        match self.container_namespace.find_closest_channel(&ns_path) {
929            Ok((root_channel, remaining_subdir)) => {
930                let (_, server_end) = create_endpoints::<fio::DirectoryMarker>();
931                fdio::open_at(
932                    &root_channel,
933                    &remaining_subdir,
934                    open_flags,
935                    server_end.into_channel(),
936                )
937                .map_err(|e| {
938                    log_error!("Failed to intialize the subdirs: {}", e);
939                    errno!(EIO)
940                })?;
941
942                Ok((fio::DirectorySynchronousProxy::new(root_channel), remaining_subdir))
943            }
944            Err(err) => {
945                log_error!(
946                    "Unable to find a channel for {}. Received error: {}",
947                    ns_path.display(),
948                    err
949                );
950                Err(errno!(ENOENT))
951            }
952        }
953    }
954
955    /// Returns an iterator of the command line arguments.
956    pub fn cmdline_args_iter(&self) -> impl Iterator<Item = ArgNameAndValue<'_>> {
957        parse_cmdline(self.cmdline.to_str().unwrap_or_default()).filter_map(|arg| {
958            arg.split_once('=')
959                .map(|(name, value)| ArgNameAndValue { name: name, value: Some(value) })
960                .or(Some(ArgNameAndValue { name: arg, value: None }))
961        })
962    }
963
964    /// Returns the container-configured CacheConfig.
965    pub fn fs_cache_config(&self) -> CacheConfig {
966        CacheConfig { capacity: self.features.dirent_cache_size as usize }
967    }
968}
969
970pub fn parse_cmdline(cmdline: &str) -> impl Iterator<Item = &str> {
971    let mut args = Vec::new();
972    let mut arg_start: Option<usize> = None;
973    let mut in_quotes = false;
974    let mut previous_char = ' ';
975
976    for (i, c) in cmdline.char_indices() {
977        if let Some(start) = arg_start {
978            match c {
979                ' ' if !in_quotes => {
980                    args.push(&cmdline[start..i]);
981                    arg_start = None;
982                }
983                '"' if previous_char != '\\' => {
984                    in_quotes = !in_quotes;
985                }
986                _ => {}
987            }
988        } else if c != ' ' {
989            arg_start = Some(i);
990            if c == '"' {
991                in_quotes = true;
992            }
993        }
994        previous_char = c;
995    }
996    if let Some(start) = arg_start {
997        args.push(&cmdline[start..]);
998    }
999    args.into_iter()
1000}
1001
1002impl std::fmt::Debug for Kernel {
1003    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1004        f.debug_struct("Kernel").finish()
1005    }
1006}
1007
1008// TODO(https://fxbug.dev/380427153): move arch dependent code to `kernel/core/arch/*`.
1009#[cfg(target_arch = "aarch64")]
1010fn arm32_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1011    use starnix_uapi::arch32;
1012    const COMPAT_ARM32_ELF_HWCAP: u32 = arch32::HWCAP_HALF
1013        | arch32::HWCAP_THUMB
1014        | arch32::HWCAP_FAST_MULT
1015        | arch32::HWCAP_EDSP
1016        | arch32::HWCAP_TLS
1017        | arch32::HWCAP_IDIV // == IDIVA | IDIVT.
1018        | arch32::HWCAP_LPAE
1019        | arch32::HWCAP_EVTSTRM;
1020
1021    let mut hwcap = COMPAT_ARM32_ELF_HWCAP;
1022    let mut hwcap2 = 0;
1023    for feature in cpu_feature_flags.iter() {
1024        match feature {
1025            CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= arch32::HWCAP_NEON,
1026            CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap2 |= arch32::HWCAP2_AES,
1027            CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap2 |= arch32::HWCAP2_PMULL,
1028            CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap2 |= arch32::HWCAP2_SHA1,
1029            CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap2 |= arch32::HWCAP2_SHA2,
1030            CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap2 |= arch32::HWCAP2_CRC32,
1031            CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap |= arch32::HWCAP_I8MM,
1032            CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= arch32::HWCAP_ASIMDFHM,
1033            CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= arch32::HWCAP_ASIMDDP,
1034            CpuFeatureFlags::ARM64_FEATURE_ISA_FP => {
1035                hwcap |= arch32::HWCAP_VFP | arch32::HWCAP_VFPv3 | arch32::HWCAP_VFPv4
1036            }
1037            _ => {}
1038        }
1039    }
1040    HwCap { hwcap, hwcap2 }
1041}
1042
1043#[cfg(target_arch = "aarch64")]
1044fn arm64_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1045    // See https://docs.kernel.org/arch/arm64/elf_hwcaps.html for details.
1046    use starnix_uapi;
1047    let mut hwcap = 0;
1048    let mut hwcap2 = 0;
1049
1050    for feature in cpu_feature_flags.iter() {
1051        match feature {
1052            CpuFeatureFlags::ARM64_FEATURE_ISA_FP => hwcap |= starnix_uapi::HWCAP_FP,
1053            CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= starnix_uapi::HWCAP_ASIMD,
1054            CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap |= starnix_uapi::HWCAP_AES,
1055            CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap |= starnix_uapi::HWCAP_PMULL,
1056            CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap |= starnix_uapi::HWCAP_SHA1,
1057            CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap |= starnix_uapi::HWCAP_SHA2,
1058            CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap |= starnix_uapi::HWCAP_CRC32,
1059            CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap2 |= starnix_uapi::HWCAP2_I8MM,
1060            CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= starnix_uapi::HWCAP_ASIMDFHM,
1061            CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= starnix_uapi::HWCAP_ASIMDDP,
1062            CpuFeatureFlags::ARM64_FEATURE_ISA_SM3 => hwcap |= starnix_uapi::HWCAP_SM3,
1063            CpuFeatureFlags::ARM64_FEATURE_ISA_SM4 => hwcap |= starnix_uapi::HWCAP_SM4,
1064            CpuFeatureFlags::ARM64_FEATURE_ISA_SHA3 => hwcap |= starnix_uapi::HWCAP_SHA3,
1065            CpuFeatureFlags::ARM64_FEATURE_ISA_SHA512 => hwcap |= starnix_uapi::HWCAP_SHA512,
1066            CpuFeatureFlags::ARM64_FEATURE_ISA_ATOMICS => hwcap |= starnix_uapi::HWCAP_ATOMICS,
1067            CpuFeatureFlags::ARM64_FEATURE_ISA_RDM => hwcap |= starnix_uapi::HWCAP_ASIMDRDM,
1068            CpuFeatureFlags::ARM64_FEATURE_ISA_TS => hwcap |= starnix_uapi::HWCAP_FLAGM,
1069            CpuFeatureFlags::ARM64_FEATURE_ISA_DPB => hwcap |= starnix_uapi::HWCAP_DCPOP,
1070            CpuFeatureFlags::ARM64_FEATURE_ISA_RNDR => hwcap2 |= starnix_uapi::HWCAP2_RNG,
1071            _ => {}
1072        }
1073    }
1074    HwCap { hwcap, hwcap2 }
1075}
1076
1077impl HwCaps {
1078    #[cfg(target_arch = "aarch64")]
1079    pub fn from_cpu_feature_flags(cpu_feature_flags: CpuFeatureFlags) -> Self {
1080        Self { arch32: arm32_hwcap(cpu_feature_flags), arch64: arm64_hwcap(cpu_feature_flags) }
1081    }
1082
1083    #[cfg(not(target_arch = "aarch64"))]
1084    pub fn from_cpu_feature_flags(_cpu_feature_flags: CpuFeatureFlags) -> Self {
1085        Self { arch64: HwCap::default() }
1086    }
1087}
1088
1089#[cfg(test)]
1090mod test {
1091    use super::parse_cmdline;
1092
1093    #[test]
1094    fn test_parse_cmdline() {
1095        let cmdline =
1096            r#"first second=third "fourth fifth" sixth="seventh eighth" "ninth\" tenth" eleventh"#;
1097        let expected = vec![
1098            "first",
1099            "second=third",
1100            "\"fourth fifth\"",
1101            "sixth=\"seventh eighth\"",
1102            "\"ninth\\\" tenth\"",
1103            "eleventh",
1104        ];
1105        assert_eq!(parse_cmdline(cmdline).collect::<Vec<_>>(), expected);
1106    }
1107}