Skip to main content

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