crosvm/crosvm/sys/linux/
jail_warden.rs

1// Copyright 2023 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Manages minijails creation after VM starts. Minijail is created during a device hotplug.
6
7#![deny(missing_docs)]
8
9use std::path::Path;
10use std::sync::Arc;
11
12use anyhow::anyhow;
13use anyhow::Context;
14use anyhow::Result;
15use base::error;
16use base::info;
17use base::syslog;
18use base::AsRawDescriptor;
19#[cfg(feature = "swap")]
20use base::AsRawDescriptors;
21use base::Pid;
22use base::Tube;
23use devices::virtio::VirtioDeviceType;
24use devices::BusDevice;
25use devices::ChildProcIntf;
26use devices::PciDevice;
27use devices::ProxyDevice;
28use devices::ResourceCarrier;
29use jail::create_base_minijail;
30use jail::create_sandbox_minijail;
31use jail::fork::fork_process;
32use jail::fork::Child;
33use jail::RunAsUser;
34use jail::SandboxConfig;
35use jail::MAX_OPEN_FILES_FOR_JAIL_WARDEN;
36use serde::Deserialize;
37use serde::Serialize;
38#[cfg(feature = "swap")]
39use swap::SwapDeviceHelper;
40use sync::Mutex;
41use vm_memory::GuestMemory;
42
43use crate::crosvm::sys::linux::pci_hotplug_helpers::build_hotplug_net_device;
44use crate::crosvm::sys::linux::pci_hotplug_helpers::NetLocalParameters;
45use crate::crosvm::sys::linux::VirtioDeviceBuilder;
46use crate::Config;
47
48/// Control commands to jail warden process.
49#[derive(Serialize, Deserialize)]
50pub enum JailCommand {
51    /// Quits jail warden process.
52    Exit,
53    /// Fork a process and create a device inside it.
54    ForkDevice(ResourceCarrier),
55}
56
57/// Response to control commands.
58#[derive(Serialize, Deserialize)]
59pub enum JailResponse {
60    /// Fork device failed with error.
61    ForkDeviceError(String),
62    /// Fork device succeeded with proxy device and keep_rds.
63    ForkDeviceOk(ChildProcIntf),
64}
65
66/// JailWarden takes ResourceCarrier, jail it, and returns a proxy to the created device.
67pub trait JailWarden {
68    /// Make a PCI device, jail it, and return the proxy to the jailed device as a BusDevice.
69    fn make_proxy_device(
70        &self,
71        resource_carrier: ResourceCarrier,
72    ) -> Result<(Arc<Mutex<dyn BusDevice>>, Pid)>;
73}
74
75/// Implementation of JailWarden
76pub struct JailWardenImpl {
77    worker_process: Option<Child>,
78    main_tube: Tube,
79}
80
81impl JailWardenImpl {
82    /// Constructor of JailWardenImpl
83    pub fn new(
84        guest_memory: GuestMemory,
85        config: &Config,
86        #[cfg(feature = "swap")] swap_device_helper: Option<SwapDeviceHelper>,
87    ) -> Result<Self> {
88        let mut keep_rds = Vec::new();
89        syslog::push_descriptors(&mut keep_rds);
90        cros_tracing::push_descriptors!(&mut keep_rds);
91        metrics::push_descriptors(&mut keep_rds);
92        let (main_tube, worker_tube) = Tube::pair()?;
93        keep_rds.push(worker_tube.as_raw_descriptor());
94        #[cfg(feature = "swap")]
95        if let Some(swap_device_helper) = &swap_device_helper {
96            keep_rds.extend(swap_device_helper.as_raw_descriptors());
97        }
98
99        let jail = match &config.jail_config {
100            Some(jail_config) => {
101                base::info!("Using sandboxed jailwarden");
102                let mut sandbox_config = SandboxConfig::new(jail_config, "jail_warden");
103                // Sandbox need to run as current user to access hotplugged devices.
104                sandbox_config.run_as = RunAsUser::CurrentUser;
105                // Caps inside sandbox needed for configuring jails.
106                sandbox_config.limit_caps = false;
107                // jail warden need access to net namespace to open network tap.
108                sandbox_config.namespace_net = false;
109                create_sandbox_minijail(
110                    Path::new("/"),
111                    MAX_OPEN_FILES_FOR_JAIL_WARDEN,
112                    &sandbox_config,
113                )
114            }
115            None => {
116                base::info!("Using base jailwarden");
117                create_base_minijail(Path::new("/"), MAX_OPEN_FILES_FOR_JAIL_WARDEN)
118            }
119        }?;
120
121        let worker_process =
122            fork_process(jail, keep_rds, Some(String::from("jail warden")), || {
123                if let Err(e) = jail_worker_process(
124                    guest_memory,
125                    worker_tube,
126                    config,
127                    #[cfg(feature = "swap")]
128                    swap_device_helper,
129                ) {
130                    error!("jail_worker_process exited with error: {:#}", e);
131                }
132            })?;
133        Ok(Self {
134            worker_process: Some(worker_process),
135            main_tube,
136        })
137    }
138}
139
140impl JailWarden for JailWardenImpl {
141    fn make_proxy_device(
142        &self,
143        resource_carrier: ResourceCarrier,
144    ) -> Result<(Arc<Mutex<dyn BusDevice>>, Pid)> {
145        self.main_tube
146            .send(&JailCommand::ForkDevice(resource_carrier))?;
147        match self.main_tube.recv::<JailResponse>()? {
148            JailResponse::ForkDeviceOk(proxy_device_primitive) => {
149                let proxy_device: ProxyDevice = proxy_device_primitive.try_into()?;
150                let pid = proxy_device.pid();
151                Ok((Arc::new(Mutex::new(proxy_device)), pid))
152            }
153            JailResponse::ForkDeviceError(e) => Err(anyhow!(e)),
154        }
155    }
156}
157
158impl Drop for JailWardenImpl {
159    fn drop(&mut self) {
160        if let Err(e) = self.main_tube.send(&JailCommand::Exit) {
161            error!("Failed to send jail warden exit command: {:?}", &e);
162        }
163        if let Some(worker_process) = self.worker_process.take() {
164            if let Err(e) = worker_process.wait() {
165                error!(
166                    "Failed to wait for jail warden worker process shutdown: {:?}",
167                    &e
168                );
169            }
170        }
171    }
172}
173
174/// The worker thread of the warden process for creating jails and locking devices inside.
175fn jail_worker_process(
176    guest_memory: GuestMemory,
177    worker_tube: Tube,
178    config: &Config,
179    #[cfg(feature = "swap")] mut swap_device_helper: Option<SwapDeviceHelper>,
180) -> Result<()> {
181    info!("JailWarden worker process started");
182
183    'worker_loop: loop {
184        match worker_tube.recv::<JailCommand>()? {
185            JailCommand::Exit => {
186                break 'worker_loop;
187            }
188            JailCommand::ForkDevice(hot_plug_device_builder) => {
189                let (pci_device, jail) = match hot_plug_device_builder {
190                    ResourceCarrier::VirtioNet(net_resource_carrier) => {
191                        let net_param = &net_resource_carrier.net_param;
192                        let jail = net_param
193                            .create_jail(config.jail_config.as_ref(), VirtioDeviceType::Regular)?
194                            .ok_or(anyhow!("no jail created"))?;
195                        let net_local_parameters =
196                            NetLocalParameters::new(guest_memory.clone(), config.protection_type);
197                        let pci_device =
198                            build_hotplug_net_device(net_resource_carrier, net_local_parameters)?;
199                        (pci_device, jail)
200                    }
201                };
202                let mut keep_rds = vec![];
203                syslog::push_descriptors(&mut keep_rds);
204                cros_tracing::push_descriptors!(&mut keep_rds);
205                metrics::push_descriptors(&mut keep_rds);
206                keep_rds.extend(pci_device.keep_rds());
207                let proxy_device_primitive = ChildProcIntf::new(
208                    pci_device,
209                    jail,
210                    keep_rds,
211                    #[cfg(feature = "swap")]
212                    &mut swap_device_helper,
213                )?;
214                worker_tube
215                    .send(&JailResponse::ForkDeviceOk(proxy_device_primitive))
216                    .context("send ChildProcIntf failed.")?;
217            }
218        }
219    }
220    Ok(())
221}
222
223/// PermissiveJailWarden act as a JailWarden, but does not jail the device.
224///
225/// PermissiveJailWarden is used when disable_sandbox flag is selected from crosvm CLI.
226pub struct PermissiveJailWarden {
227    protection_type: hypervisor::ProtectionType,
228    guest_memory: GuestMemory,
229}
230
231impl PermissiveJailWarden {
232    /// Constructor of PermissiveJailWarden
233    pub fn new(
234        guest_memory: GuestMemory,
235        config: &Config,
236        #[cfg(feature = "swap")] _swap_device_helper: Option<SwapDeviceHelper>,
237    ) -> Result<Self> {
238        Ok(Self {
239            protection_type: config.protection_type,
240            guest_memory,
241        })
242    }
243}
244
245impl JailWarden for PermissiveJailWarden {
246    fn make_proxy_device(
247        &self,
248        resource_carrier: ResourceCarrier,
249    ) -> Result<(Arc<Mutex<dyn BusDevice>>, Pid)> {
250        let pci_device = match resource_carrier {
251            ResourceCarrier::VirtioNet(net_resource_carrier) => {
252                let net_local_parameters =
253                    NetLocalParameters::new(self.guest_memory.clone(), self.protection_type);
254                build_hotplug_net_device(net_resource_carrier, net_local_parameters)?
255            }
256        };
257        Ok((Arc::new(Mutex::new(pci_device)), 0))
258    }
259}