1 // Copyright 2021, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Implementation of the AIDL interface of the VirtualizationService.
16 
17 use crate::{get_calling_pid, get_calling_uid, get_this_pid};
18 use crate::atom::{write_vm_booted_stats, write_vm_creation_stats};
19 use crate::composite::make_composite_image;
20 use crate::crosvm::{AudioConfig, CrosvmConfig, DiskFile, SharedPathConfig, DisplayConfig, GpuConfig, InputDeviceOption, PayloadState, UsbConfig, VmContext, VmInstance, VmState};
21 use crate::debug_config::DebugConfig;
22 use crate::dt_overlay::{create_device_tree_overlay, VM_DT_OVERLAY_MAX_SIZE, VM_DT_OVERLAY_PATH};
23 use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images, add_microdroid_vendor_image};
24 use crate::selinux::{check_tee_service_permission, getfilecon, getprevcon, SeContext};
25 use android_os_permissions_aidl::aidl::android::os::IPermissionController;
26 use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::{
27     Certificate::Certificate,
28     DeathReason::DeathReason,
29     ErrorCode::ErrorCode,
30 };
31 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
32     AssignableDevice::AssignableDevice,
33     CpuTopology::CpuTopology,
34     DiskImage::DiskImage,
35     SharedPath::SharedPath,
36     InputDevice::InputDevice,
37     IVirtualMachine::{self, BnVirtualMachine},
38     IVirtualMachineCallback::IVirtualMachineCallback,
39     IVirtualizationService::IVirtualizationService,
40     Partition::Partition,
41     PartitionType::PartitionType,
42     VirtualMachineAppConfig::{DebugLevel::DebugLevel, Payload::Payload, VirtualMachineAppConfig},
43     VirtualMachineConfig::VirtualMachineConfig,
44     VirtualMachineDebugInfo::VirtualMachineDebugInfo,
45     VirtualMachinePayloadConfig::VirtualMachinePayloadConfig,
46     VirtualMachineRawConfig::VirtualMachineRawConfig,
47     VirtualMachineState::VirtualMachineState,
48 };
49 use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IGlobalVmContext::IGlobalVmContext;
50 use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IVirtualizationServiceInternal::IVirtualizationServiceInternal;
51 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
52         BnVirtualMachineService, IVirtualMachineService,
53 };
54 use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::ISecretkeeper::{BnSecretkeeper, ISecretkeeper};
55 use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::SecretId::SecretId;
56 use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::PublicKey::PublicKey;
57 use android_hardware_security_authgraph::aidl::android::hardware::security::authgraph::{
58     Arc::Arc as AuthgraphArc, IAuthGraphKeyExchange::IAuthGraphKeyExchange,
59     IAuthGraphKeyExchange::BnAuthGraphKeyExchange, Identity::Identity, KeInitResult::KeInitResult,
60     Key::Key, PubKey::PubKey, SessionIdSignature::SessionIdSignature, SessionInfo::SessionInfo,
61     SessionInitiationInfo::SessionInitiationInfo,
62 };
63 use anyhow::{anyhow, bail, ensure, Context, Result};
64 use apkverify::{HashAlgorithm, V4Signature};
65 use avflog::LogResult;
66 use binder::{
67     self, wait_for_interface, Accessor, BinderFeatures, ConnectionInfo, ExceptionCode, Interface, ParcelFileDescriptor,
68     SpIBinder, Status, StatusCode, Strong, IntoBinderResult,
69 };
70 use cstr::cstr;
71 use glob::glob;
72 use libc::{AF_VSOCK, sa_family_t, sockaddr_vm};
73 use log::{debug, error, info, warn};
74 use microdroid_payload_config::{ApkConfig, Task, TaskType, VmPayloadConfig};
75 use nix::unistd::pipe;
76 use rpcbinder::RpcServer;
77 use rustutils::system_properties;
78 use semver::VersionReq;
79 use serde::Deserialize;
80 use std::collections::{HashSet, HashMap};
81 use std::convert::TryInto;
82 use std::fs;
83 use std::ffi::CStr;
84 use std::fs::{canonicalize, create_dir_all, read_dir, remove_dir_all, remove_file, File, OpenOptions};
85 use std::io::{BufRead, BufReader, Error, ErrorKind, Seek, SeekFrom, Write};
86 use std::iter;
87 use std::num::{NonZeroU16, NonZeroU32};
88 use std::ops::Range;
89 use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
90 use std::os::unix::raw::pid_t;
91 use std::path::{Path, PathBuf};
92 use std::sync::{Arc, Mutex, Weak, LazyLock};
93 use vbmeta::VbMetaImage;
94 use vmconfig::{VmConfig, get_debug_level};
95 use vsock::VsockStream;
96 use zip::ZipArchive;
97 
98 /// The unique ID of a VM used (together with a port number) for vsock communication.
99 pub type Cid = u32;
100 
101 pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
102 
103 /// Vsock privileged ports are below this number.
104 const VSOCK_PRIV_PORT_MAX: u32 = 1024;
105 
106 /// The size of zero.img.
107 /// Gaps in composite disk images are filled with a shared zero.img.
108 const ZERO_FILLER_SIZE: u64 = 4096;
109 
110 /// Magic string for the instance image
111 const ANDROID_VM_INSTANCE_MAGIC: &str = "Android-VM-instance";
112 
113 /// Version of the instance image format
114 const ANDROID_VM_INSTANCE_VERSION: u16 = 1;
115 
116 const MICRODROID_OS_NAME: &str = "microdroid";
117 
118 const SECRETKEEPER_IDENTIFIER: &str =
119     "android.hardware.security.secretkeeper.ISecretkeeper/default";
120 
121 const UNFORMATTED_STORAGE_MAGIC: &str = "UNFORMATTED-STORAGE";
122 
123 /// crosvm requires all partitions to be a multiple of 4KiB.
124 const PARTITION_GRANULARITY_BYTES: u64 = 4096;
125 
126 const VM_REFERENCE_DT_ON_HOST_PATH: &str = "/proc/device-tree/avf/reference";
127 
128 pub static GLOBAL_SERVICE: LazyLock<Strong<dyn IVirtualizationServiceInternal>> =
129     LazyLock::new(|| {
130         if cfg!(early) {
131             panic!("Early virtmgr must not connect to VirtualizatinoServiceInternal")
132         } else {
133             wait_for_interface(BINDER_SERVICE_IDENTIFIER)
134                 .expect("Could not connect to VirtualizationServiceInternal")
135         }
136     });
137 static SUPPORTED_OS_NAMES: LazyLock<HashSet<String>> =
138     LazyLock::new(|| get_supported_os_names().expect("Failed to get list of supported os names"));
139 
create_or_update_idsig_file( input_fd: &ParcelFileDescriptor, idsig_fd: &ParcelFileDescriptor, ) -> Result<()>140 fn create_or_update_idsig_file(
141     input_fd: &ParcelFileDescriptor,
142     idsig_fd: &ParcelFileDescriptor,
143 ) -> Result<()> {
144     let mut input = clone_file(input_fd)?;
145     let metadata = input.metadata().context("failed to get input metadata")?;
146     if !metadata.is_file() {
147         bail!("input is not a regular file");
148     }
149     let mut sig =
150         V4Signature::create(&mut input, get_current_sdk()?, 4096, &[], HashAlgorithm::SHA256)
151             .context("failed to create idsig")?;
152 
153     let mut output = clone_file(idsig_fd)?;
154 
155     // Optimization. We don't have to update idsig file whenever a VM is started. Don't update it,
156     // if the idsig file already has the same APK digest.
157     if output.metadata()?.len() > 0 {
158         if let Ok(out_sig) = V4Signature::from_idsig(&mut output) {
159             if out_sig.signing_info.apk_digest == sig.signing_info.apk_digest {
160                 debug!("idsig {:?} is up-to-date with apk {:?}.", output, input);
161                 return Ok(());
162             }
163         }
164         // if we fail to read v4signature from output, that's fine. User can pass a random file.
165         // We will anyway overwrite the file to the v4signature generated from input_fd.
166     }
167 
168     output
169         .seek(SeekFrom::Start(0))
170         .context("failed to move cursor to start on the idsig output")?;
171     output.set_len(0).context("failed to set_len on the idsig output")?;
172     sig.write_into(&mut output).context("failed to write idsig")?;
173     Ok(())
174 }
175 
get_current_sdk() -> Result<u32>176 fn get_current_sdk() -> Result<u32> {
177     let current_sdk = system_properties::read("ro.build.version.sdk")?;
178     let current_sdk = current_sdk.ok_or_else(|| anyhow!("SDK version missing"))?;
179     current_sdk.parse().context("Malformed SDK version")
180 }
181 
remove_temporary_files(path: &PathBuf) -> Result<()>182 pub fn remove_temporary_files(path: &PathBuf) -> Result<()> {
183     for dir_entry in read_dir(path)? {
184         remove_file(dir_entry?.path())?;
185     }
186     Ok(())
187 }
188 
189 /// Implementation of `IVirtualizationService`, the entry point of the AIDL service.
190 #[derive(Debug, Default)]
191 pub struct VirtualizationService {
192     state: Arc<Mutex<State>>,
193 }
194 
195 impl Interface for VirtualizationService {
dump(&self, writer: &mut dyn Write, _args: &[&CStr]) -> Result<(), StatusCode>196     fn dump(&self, writer: &mut dyn Write, _args: &[&CStr]) -> Result<(), StatusCode> {
197         check_permission("android.permission.DUMP").or(Err(StatusCode::PERMISSION_DENIED))?;
198         let state = &mut *self.state.lock().unwrap();
199         let vms = state.vms();
200         writeln!(writer, "Running {0} VMs:", vms.len()).or(Err(StatusCode::UNKNOWN_ERROR))?;
201         for vm in vms {
202             writeln!(writer, "VM CID: {}", vm.cid).or(Err(StatusCode::UNKNOWN_ERROR))?;
203             writeln!(writer, "\tState: {:?}", vm.vm_state.lock().unwrap())
204                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
205             writeln!(writer, "\tPayload state {:?}", vm.payload_state())
206                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
207             writeln!(writer, "\tProtected: {}", vm.protected).or(Err(StatusCode::UNKNOWN_ERROR))?;
208             writeln!(writer, "\ttemporary_directory: {}", vm.temporary_directory.to_string_lossy())
209                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
210             writeln!(writer, "\trequester_uid: {}", vm.requester_uid)
211                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
212             writeln!(writer, "\trequester_debug_pid: {}", vm.requester_debug_pid)
213                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
214         }
215         Ok(())
216     }
217 }
218 impl IVirtualizationService for VirtualizationService {
219     /// Creates (but does not start) a new VM with the given configuration, assigning it the next
220     /// available CID.
221     ///
222     /// Returns a binder `IVirtualMachine` object referring to it, as a handle for the client.
createVm( &self, config: &VirtualMachineConfig, console_out_fd: Option<&ParcelFileDescriptor>, console_in_fd: Option<&ParcelFileDescriptor>, log_fd: Option<&ParcelFileDescriptor>, dump_dt_fd: Option<&ParcelFileDescriptor>, ) -> binder::Result<Strong<dyn IVirtualMachine::IVirtualMachine>>223     fn createVm(
224         &self,
225         config: &VirtualMachineConfig,
226         console_out_fd: Option<&ParcelFileDescriptor>,
227         console_in_fd: Option<&ParcelFileDescriptor>,
228         log_fd: Option<&ParcelFileDescriptor>,
229         dump_dt_fd: Option<&ParcelFileDescriptor>,
230     ) -> binder::Result<Strong<dyn IVirtualMachine::IVirtualMachine>> {
231         let mut is_protected = false;
232         let ret = self.create_vm_internal(
233             config,
234             console_out_fd,
235             console_in_fd,
236             log_fd,
237             &mut is_protected,
238             dump_dt_fd,
239         );
240         write_vm_creation_stats(config, is_protected, &ret);
241         ret
242     }
243 
244     /// Allocate a new instance_id to the VM
allocateInstanceId(&self) -> binder::Result<[u8; 64]>245     fn allocateInstanceId(&self) -> binder::Result<[u8; 64]> {
246         check_manage_access()?;
247         GLOBAL_SERVICE.allocateInstanceId()
248     }
249 
250     /// Initialise an empty partition image of the given size to be used as a writable partition.
initializeWritablePartition( &self, image_fd: &ParcelFileDescriptor, size_bytes: i64, partition_type: PartitionType, ) -> binder::Result<()>251     fn initializeWritablePartition(
252         &self,
253         image_fd: &ParcelFileDescriptor,
254         size_bytes: i64,
255         partition_type: PartitionType,
256     ) -> binder::Result<()> {
257         check_manage_access()?;
258         let size_bytes = size_bytes
259             .try_into()
260             .with_context(|| format!("Invalid size: {}", size_bytes))
261             .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?;
262         let size_bytes = round_up(size_bytes, PARTITION_GRANULARITY_BYTES);
263         let mut image = clone_file(image_fd)?;
264         // initialize the file. Any data in the file will be erased.
265         image
266             .seek(SeekFrom::Start(0))
267             .context("failed to move cursor to start")
268             .or_service_specific_exception(-1)?;
269         image.set_len(0).context("Failed to reset a file").or_service_specific_exception(-1)?;
270         // Set the file length. In most filesystems, this will not allocate any physical disk
271         // space, it will only change the logical size.
272         image
273             .set_len(size_bytes)
274             .context("Failed to extend file")
275             .or_service_specific_exception(-1)?;
276 
277         match partition_type {
278             PartitionType::RAW => Ok(()),
279             PartitionType::ANDROID_VM_INSTANCE => format_as_android_vm_instance(&mut image),
280             PartitionType::ENCRYPTEDSTORE => format_as_encryptedstore(&mut image),
281             _ => Err(Error::new(
282                 ErrorKind::Unsupported,
283                 format!("Unsupported partition type {:?}", partition_type),
284             )),
285         }
286         .with_context(|| format!("Failed to initialize partition as {:?}", partition_type))
287         .or_service_specific_exception(-1)?;
288 
289         Ok(())
290     }
291 
292     /// Creates or update the idsig file by digesting the input APK file.
createOrUpdateIdsigFile( &self, input_fd: &ParcelFileDescriptor, idsig_fd: &ParcelFileDescriptor, ) -> binder::Result<()>293     fn createOrUpdateIdsigFile(
294         &self,
295         input_fd: &ParcelFileDescriptor,
296         idsig_fd: &ParcelFileDescriptor,
297     ) -> binder::Result<()> {
298         check_manage_access()?;
299 
300         create_or_update_idsig_file(input_fd, idsig_fd).or_service_specific_exception(-1)?;
301         Ok(())
302     }
303 
304     /// Get a list of all currently running VMs. This method is only intended for debug purposes,
305     /// and as such is only permitted from the shell user.
debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>>306     fn debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>> {
307         // Delegate to the global service, including checking the debug permission.
308         GLOBAL_SERVICE.debugListVms()
309     }
310 
311     /// Get a list of assignable device types.
getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>>312     fn getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>> {
313         // Delegate to the global service, including checking the permission.
314         GLOBAL_SERVICE.getAssignableDevices()
315     }
316 
317     /// Get a list of supported OSes.
getSupportedOSList(&self) -> binder::Result<Vec<String>>318     fn getSupportedOSList(&self) -> binder::Result<Vec<String>> {
319         Ok(Vec::from_iter(SUPPORTED_OS_NAMES.iter().cloned()))
320     }
321 
322     /// Returns whether given feature is enabled
isFeatureEnabled(&self, feature: &str) -> binder::Result<bool>323     fn isFeatureEnabled(&self, feature: &str) -> binder::Result<bool> {
324         check_manage_access()?;
325         Ok(avf_features::is_feature_enabled(feature))
326     }
327 
enableTestAttestation(&self) -> binder::Result<()>328     fn enableTestAttestation(&self) -> binder::Result<()> {
329         GLOBAL_SERVICE.enableTestAttestation()
330     }
331 
isRemoteAttestationSupported(&self) -> binder::Result<bool>332     fn isRemoteAttestationSupported(&self) -> binder::Result<bool> {
333         check_manage_access()?;
334         GLOBAL_SERVICE.isRemoteAttestationSupported()
335     }
336 
isUpdatableVmSupported(&self) -> binder::Result<bool>337     fn isUpdatableVmSupported(&self) -> binder::Result<bool> {
338         // The response is specific to Microdroid. Updatable VMs are only possible if device
339         // supports Secretkeeper. Guest OS needs to use Secretkeeper based secrets. Microdroid does
340         // this, however other guest OSes may do things differently.
341         check_manage_access()?;
342         Ok(is_secretkeeper_supported())
343     }
344 
removeVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()>345     fn removeVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()> {
346         check_manage_access()?;
347         GLOBAL_SERVICE.removeVmInstance(instance_id)
348     }
349 
claimVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()>350     fn claimVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()> {
351         check_manage_access()?;
352         GLOBAL_SERVICE.claimVmInstance(instance_id)
353     }
354 }
355 
356 /// Implementation of the AIDL `IGlobalVmContext` interface for early VMs.
357 #[derive(Debug, Default)]
358 struct EarlyVmContext {
359     /// The unique CID assigned to the VM for vsock communication.
360     cid: Cid,
361     /// Temporary directory for this VM instance.
362     temp_dir: PathBuf,
363 }
364 
365 impl EarlyVmContext {
new(cid: Cid, temp_dir: PathBuf) -> Result<Self>366     fn new(cid: Cid, temp_dir: PathBuf) -> Result<Self> {
367         // Remove the entire directory before creating a VM. Early VMs use predefined CIDs and AVF
368         // should trust clients, e.g. they won't run two VMs at the same time
369         let _ = remove_dir_all(&temp_dir);
370         create_dir_all(&temp_dir).context(format!("can't create '{}'", temp_dir.display()))?;
371 
372         Ok(Self { cid, temp_dir })
373     }
374 }
375 
376 impl Interface for EarlyVmContext {}
377 
378 impl Drop for EarlyVmContext {
drop(&mut self)379     fn drop(&mut self) {
380         if let Err(e) = remove_dir_all(&self.temp_dir) {
381             error!("Cannot remove {} upon dropping: {e}", self.temp_dir.display());
382         }
383     }
384 }
385 
386 impl IGlobalVmContext for EarlyVmContext {
getCid(&self) -> binder::Result<i32>387     fn getCid(&self) -> binder::Result<i32> {
388         Ok(self.cid as i32)
389     }
390 
getTemporaryDirectory(&self) -> binder::Result<String>391     fn getTemporaryDirectory(&self) -> binder::Result<String> {
392         Ok(self.temp_dir.to_string_lossy().to_string())
393     }
394 
setHostConsoleName(&self, _pathname: &str) -> binder::Result<()>395     fn setHostConsoleName(&self, _pathname: &str) -> binder::Result<()> {
396         Err(Status::new_exception_str(
397             ExceptionCode::UNSUPPORTED_OPERATION,
398             Some("Early VM doesn't support setting host console name"),
399         ))
400     }
401 }
402 
find_partition(path: &Path) -> binder::Result<String>403 fn find_partition(path: &Path) -> binder::Result<String> {
404     match path.components().nth(1) {
405         Some(std::path::Component::Normal(partition)) => {
406             Ok(partition.to_string_lossy().into_owned())
407         }
408         _ => Err(anyhow!("Can't find partition in '{}'", path.display()))
409             .or_service_specific_exception(-1),
410     }
411 }
412 
413 impl VirtualizationService {
init() -> VirtualizationService414     pub fn init() -> VirtualizationService {
415         VirtualizationService::default()
416     }
417 
create_early_vm_context( &self, config: &VirtualMachineConfig, ) -> binder::Result<(VmContext, Cid, PathBuf)>418     fn create_early_vm_context(
419         &self,
420         config: &VirtualMachineConfig,
421     ) -> binder::Result<(VmContext, Cid, PathBuf)> {
422         let calling_exe_path = format!("/proc/{}/exe", get_calling_pid());
423         let link = fs::read_link(&calling_exe_path)
424             .context(format!("can't read_link '{calling_exe_path}'"))
425             .or_service_specific_exception(-1)?;
426         let partition = find_partition(&link)?;
427 
428         let name = match config {
429             VirtualMachineConfig::RawConfig(config) => &config.name,
430             VirtualMachineConfig::AppConfig(config) => &config.name,
431         };
432         let early_vm =
433             find_early_vm_for_partition(&partition, name).or_service_specific_exception(-1)?;
434         if Path::new(&early_vm.path) != link {
435             return Err(anyhow!(
436                 "VM '{name}' in partition '{partition}' must be created with '{}', not '{}'",
437                 &early_vm.path,
438                 link.display()
439             ))
440             .or_service_specific_exception(-1);
441         }
442 
443         let cid = early_vm.cid as Cid;
444         let temp_dir = PathBuf::from(format!("/mnt/vm/early/{cid}"));
445 
446         let context = EarlyVmContext::new(cid, temp_dir.clone())
447             .context(format!("Can't create early vm contexts for {cid}"))
448             .or_service_specific_exception(-1)?;
449 
450         if requires_vm_service(config) {
451             // Start VM service listening for connections from the new CID on port=CID.
452             let service = VirtualMachineService::new_binder(self.state.clone(), cid).as_binder();
453             let port = cid;
454             let (vm_server, _) = RpcServer::new_vsock(service, cid, port)
455                 .context(format!("Could not start RpcServer on port {port}"))
456                 .or_service_specific_exception(-1)?;
457             vm_server.start();
458             Ok((VmContext::new(Strong::new(Box::new(context)), Some(vm_server)), cid, temp_dir))
459         } else {
460             Ok((VmContext::new(Strong::new(Box::new(context)), None), cid, temp_dir))
461         }
462     }
463 
create_vm_context( &self, requester_debug_pid: pid_t, config: &VirtualMachineConfig, ) -> binder::Result<(VmContext, Cid, PathBuf)>464     fn create_vm_context(
465         &self,
466         requester_debug_pid: pid_t,
467         config: &VirtualMachineConfig,
468     ) -> binder::Result<(VmContext, Cid, PathBuf)> {
469         const NUM_ATTEMPTS: usize = 5;
470 
471         for _ in 0..NUM_ATTEMPTS {
472             let vm_context = GLOBAL_SERVICE.allocateGlobalVmContext(requester_debug_pid)?;
473             let cid = vm_context.getCid()? as Cid;
474             let temp_dir: PathBuf = vm_context.getTemporaryDirectory()?.into();
475 
476             // We don't need to start the VM service for custom VMs.
477             if !requires_vm_service(config) {
478                 return Ok((VmContext::new(vm_context, None), cid, temp_dir));
479             }
480 
481             let service = VirtualMachineService::new_binder(self.state.clone(), cid).as_binder();
482 
483             // Start VM service listening for connections from the new CID on port=CID.
484             let port = cid;
485             match RpcServer::new_vsock(service, cid, port) {
486                 Ok((vm_server, _)) => {
487                     vm_server.start();
488                     return Ok((VmContext::new(vm_context, Some(vm_server)), cid, temp_dir));
489                 }
490                 Err(err) => {
491                     warn!("Could not start RpcServer on port {}: {}", port, err);
492                 }
493             }
494         }
495         Err(anyhow!("Too many attempts to create VM context failed"))
496             .or_service_specific_exception(-1)
497     }
498 
create_vm_internal( &self, config: &VirtualMachineConfig, console_out_fd: Option<&ParcelFileDescriptor>, console_in_fd: Option<&ParcelFileDescriptor>, log_fd: Option<&ParcelFileDescriptor>, is_protected: &mut bool, dump_dt_fd: Option<&ParcelFileDescriptor>, ) -> binder::Result<Strong<dyn IVirtualMachine::IVirtualMachine>>499     fn create_vm_internal(
500         &self,
501         config: &VirtualMachineConfig,
502         console_out_fd: Option<&ParcelFileDescriptor>,
503         console_in_fd: Option<&ParcelFileDescriptor>,
504         log_fd: Option<&ParcelFileDescriptor>,
505         is_protected: &mut bool,
506         dump_dt_fd: Option<&ParcelFileDescriptor>,
507     ) -> binder::Result<Strong<dyn IVirtualMachine::IVirtualMachine>> {
508         let requester_uid = get_calling_uid();
509         let requester_debug_pid = get_calling_pid();
510 
511         check_config_features(config)?;
512 
513         if cfg!(early) {
514             check_config_allowed_for_early_vms(config)?;
515         }
516 
517         let caller_secontext = getprevcon().or_service_specific_exception(-1)?;
518         info!("callers secontext: {}", caller_secontext);
519 
520         // Allocating VM context checks the MANAGE_VIRTUAL_MACHINE permission.
521         let (vm_context, cid, temporary_directory) = if cfg!(early) {
522             self.create_early_vm_context(config)?
523         } else {
524             self.create_vm_context(requester_debug_pid, config)?
525         };
526 
527         if is_custom_config(config) {
528             check_use_custom_virtual_machine()?;
529         }
530 
531         let gdb_port = extract_gdb_port(config);
532 
533         // Additional permission checks if caller request gdb.
534         if gdb_port.is_some() {
535             check_gdb_allowed(config)?;
536         }
537 
538         let device_tree_overlay = maybe_create_device_tree_overlay(config, &temporary_directory)?;
539 
540         let debug_config = DebugConfig::new(config);
541         let ramdump = if !uses_gki_kernel(config) && debug_config.is_ramdump_needed() {
542             Some(prepare_ramdump_file(&temporary_directory)?)
543         } else {
544             None
545         };
546 
547         let state = &mut *self.state.lock().unwrap();
548         let console_out_fd =
549             clone_or_prepare_logger_fd(console_out_fd, format!("Console({})", cid))?;
550         let console_in_fd = console_in_fd.map(clone_file).transpose()?;
551         let log_fd = clone_or_prepare_logger_fd(log_fd, format!("Log({})", cid))?;
552         let dump_dt_fd = if let Some(fd) = dump_dt_fd {
553             Some(clone_file(fd)?)
554         } else if debug_config.dump_device_tree {
555             Some(prepare_dump_dt_file(&temporary_directory)?)
556         } else {
557             None
558         };
559 
560         // Counter to generate unique IDs for temporary image files.
561         let mut next_temporary_image_id = 0;
562         // Files which are referred to from composite images. These must be mapped to the crosvm
563         // child process, and not closed before it is started.
564         let mut indirect_files = vec![];
565 
566         let (is_app_config, config) = match config {
567             VirtualMachineConfig::RawConfig(config) => (false, BorrowedOrOwned::Borrowed(config)),
568             VirtualMachineConfig::AppConfig(config) => {
569                 let config = load_app_config(config, &debug_config, &temporary_directory)
570                     .or_service_specific_exception_with(-1, |e| {
571                         *is_protected = config.protectedVm;
572                         let message = format!("Failed to load app config: {:?}", e);
573                         error!("{}", message);
574                         message
575                     })?;
576                 (true, BorrowedOrOwned::Owned(config))
577             }
578         };
579         let config = config.as_ref();
580         *is_protected = config.protectedVm;
581 
582         if !config.teeServices.is_empty() {
583             check_tee_service_permission(&caller_secontext, &config.teeServices)
584                 .with_log()
585                 .or_binder_exception(ExceptionCode::SECURITY)?;
586         }
587 
588         let kernel = maybe_clone_file(&config.kernel)?;
589         let initrd = maybe_clone_file(&config.initrd)?;
590 
591         if config.protectedVm {
592             // Fail fast with a meaningful error message in case device doesn't support pVMs.
593             check_protected_vm_is_supported()?;
594 
595             // In a protected VM, we require custom kernels to come from a trusted source
596             // (b/237054515).
597             check_label_for_kernel_files(&kernel, &initrd).or_service_specific_exception(-1)?;
598 
599             // Check if partition images are labeled incorrectly. This is to prevent random images
600             // which are not protected by the Android Verified Boot (e.g. bits downloaded by apps)
601             // from being loaded in a pVM. This applies to everything but the instance image in the
602             // raw config, and everything but the non-executable, generated partitions in the app
603             // config.
604             config
605                 .disks
606                 .iter()
607                 .flat_map(|disk| disk.partitions.iter())
608                 .filter(|partition| {
609                     if is_app_config {
610                         !is_safe_app_partition(&partition.label)
611                     } else {
612                         !is_safe_raw_partition(&partition.label)
613                     }
614                 })
615                 .try_for_each(check_label_for_partition)
616                 .or_service_specific_exception(-1)?;
617         }
618 
619         // Check if files for payloads and bases are NOT coming from /vendor and /odm, as they may
620         // have unstable interfaces.
621         // TODO(b/316431494): remove once Treble interfaces are stabilized.
622         check_partitions_for_files(config).or_service_specific_exception(-1)?;
623 
624         let zero_filler_path = temporary_directory.join("zero.img");
625         write_zero_filler(&zero_filler_path)
626             .context("Failed to make composite image")
627             .with_log()
628             .or_service_specific_exception(-1)?;
629 
630         // Assemble disk images if needed.
631         let disks = config
632             .disks
633             .iter()
634             .map(|disk| {
635                 assemble_disk_image(
636                     disk,
637                     &zero_filler_path,
638                     &temporary_directory,
639                     &mut next_temporary_image_id,
640                     &mut indirect_files,
641                 )
642             })
643             .collect::<Result<Vec<DiskFile>, _>>()?;
644 
645         let shared_paths = assemble_shared_paths(&config.sharedPaths, &temporary_directory)?;
646 
647         let (cpus, host_cpu_topology) = match config.cpuTopology {
648             CpuTopology::MATCH_HOST => (None, true),
649             CpuTopology::ONE_CPU => (NonZeroU32::new(1), false),
650             val => {
651                 return Err(anyhow!("Failed to parse CPU topology value {:?}", val))
652                     .with_log()
653                     .or_service_specific_exception(-1);
654             }
655         };
656 
657         let (vfio_devices, dtbo) = if !config.devices.is_empty() {
658             let mut set = HashSet::new();
659             for device in config.devices.iter() {
660                 let path = canonicalize(device)
661                     .with_context(|| format!("can't canonicalize {device}"))
662                     .or_service_specific_exception(-1)?;
663                 if !set.insert(path) {
664                     return Err(anyhow!("duplicated device {device}"))
665                         .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
666                 }
667             }
668             let devices = GLOBAL_SERVICE.bindDevicesToVfioDriver(&config.devices)?;
669             let dtbo_file = File::from(
670                 GLOBAL_SERVICE
671                     .getDtboFile()?
672                     .as_ref()
673                     .try_clone()
674                     .context("Failed to create VM DTBO from ParcelFileDescriptor")
675                     .or_binder_exception(ExceptionCode::BAD_PARCELABLE)?,
676             );
677             (devices, Some(dtbo_file))
678         } else {
679             (vec![], None)
680         };
681         let display_config = if cfg!(paravirtualized_devices) {
682             config
683                 .displayConfig
684                 .as_ref()
685                 .map(DisplayConfig::new)
686                 .transpose()
687                 .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?
688         } else {
689             None
690         };
691         let gpu_config = if cfg!(paravirtualized_devices) {
692             config
693                 .gpuConfig
694                 .as_ref()
695                 .map(GpuConfig::new)
696                 .transpose()
697                 .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?
698         } else {
699             None
700         };
701 
702         let input_device_options = if cfg!(paravirtualized_devices) {
703             config
704                 .inputDevices
705                 .iter()
706                 .map(to_input_device_option_from)
707                 .collect::<Result<Vec<InputDeviceOption>, _>>()
708                 .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?
709         } else {
710             vec![]
711         };
712 
713         // Create TAP network interface if the VM supports network.
714         let tap = if cfg!(network) && config.networkSupported {
715             if *is_protected {
716                 return Err(anyhow!("Network feature is not supported for pVM yet"))
717                     .with_log()
718                     .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION)?;
719             }
720             Some(File::from(
721                 GLOBAL_SERVICE
722                     .createTapInterface(&get_this_pid().to_string())?
723                     .as_ref()
724                     .try_clone()
725                     .context("Failed to get TAP interface from ParcelFileDescriptor")
726                     .or_binder_exception(ExceptionCode::BAD_PARCELABLE)?,
727             ))
728         } else {
729             None
730         };
731 
732         let audio_config = if cfg!(paravirtualized_devices) {
733             config.audioConfig.as_ref().map(AudioConfig::new)
734         } else {
735             None
736         };
737 
738         let usb_config = config
739             .usbConfig
740             .as_ref()
741             .map(UsbConfig::new)
742             .unwrap_or(Ok(UsbConfig { controller: false }))
743             .or_binder_exception(ExceptionCode::BAD_PARCELABLE)?;
744 
745         // Actually start the VM.
746         let crosvm_config = CrosvmConfig {
747             cid,
748             name: config.name.clone(),
749             bootloader: maybe_clone_file(&config.bootloader)?,
750             kernel,
751             initrd,
752             disks,
753             shared_paths,
754             params: config.params.to_owned(),
755             protected: *is_protected,
756             debug_config,
757             memory_mib: config
758                 .memoryMib
759                 .try_into()
760                 .ok()
761                 .and_then(NonZeroU32::new)
762                 .unwrap_or(NonZeroU32::new(256).unwrap()),
763             cpus,
764             host_cpu_topology,
765             console_out_fd,
766             console_in_fd,
767             log_fd,
768             ramdump,
769             indirect_files,
770             platform_version: parse_platform_version_req(&config.platformVersion)?,
771             detect_hangup: is_app_config,
772             gdb_port,
773             vfio_devices,
774             dtbo,
775             device_tree_overlay,
776             display_config,
777             input_device_options,
778             hugepages: config.hugePages,
779             tap,
780             console_input_device: config.consoleInputDevice.clone(),
781             boost_uclamp: config.boostUclamp,
782             gpu_config,
783             audio_config,
784             no_balloon: config.noBalloon,
785             usb_config,
786             dump_dt_fd,
787         };
788         let instance = Arc::new(
789             VmInstance::new(
790                 crosvm_config,
791                 temporary_directory,
792                 requester_uid,
793                 requester_debug_pid,
794                 vm_context,
795             )
796             .with_context(|| format!("Failed to create VM with config {:?}", config))
797             .with_log()
798             .or_service_specific_exception(-1)?,
799         );
800         state.add_vm(Arc::downgrade(&instance));
801         Ok(VirtualMachine::create(instance))
802     }
803 }
804 
805 /// Returns whether a VM config represents a "custom" virtual machine, which requires the
806 /// USE_CUSTOM_VIRTUAL_MACHINE.
is_custom_config(config: &VirtualMachineConfig) -> bool807 fn is_custom_config(config: &VirtualMachineConfig) -> bool {
808     match config {
809         // Any raw (non-Microdroid) VM is considered custom.
810         VirtualMachineConfig::RawConfig(_) => true,
811         VirtualMachineConfig::AppConfig(config) => {
812             // Some features are reserved for platform apps only, even when using
813             // VirtualMachineAppConfig. Almost all of these features are grouped in the
814             // CustomConfig struct:
815             // - controlling CPUs;
816             // - gdbPort is set, meaning that crosvm will start a gdb server;
817             // - using anything other than the default kernel;
818             // - specifying devices to be assigned.
819             if config.customConfig.is_some() {
820                 true
821             } else {
822                 // Additional custom features not included in CustomConfig:
823                 // - specifying a config file;
824                 // - specifying extra APKs;
825                 // - specifying an OS other than Microdroid.
826                 (match &config.payload {
827                     Payload::ConfigPath(_) => true,
828                     Payload::PayloadConfig(payload_config) => !payload_config.extraApks.is_empty(),
829                 }) || config.osName != MICRODROID_OS_NAME
830             }
831         }
832     }
833 }
834 
835 /// Returns whether a VM config requires VirtualMachineService running on the host. Only Microdroid
836 /// VM (i.e. AppConfig) requires it. However, a few Microdroid tests use RawConfig for Microdroid
837 /// VM. To handle the exceptional case, we use name as a second criteria; if the name is
838 /// "microdroid" we run VirtualMachineService
requires_vm_service(config: &VirtualMachineConfig) -> bool839 fn requires_vm_service(config: &VirtualMachineConfig) -> bool {
840     match config {
841         VirtualMachineConfig::AppConfig(_) => true,
842         VirtualMachineConfig::RawConfig(config) => config.name == "microdroid",
843     }
844 }
845 
extract_vendor_hashtree_digest(config: &VirtualMachineConfig) -> Result<Option<Vec<u8>>>846 fn extract_vendor_hashtree_digest(config: &VirtualMachineConfig) -> Result<Option<Vec<u8>>> {
847     let VirtualMachineConfig::AppConfig(config) = config else {
848         return Ok(None);
849     };
850     let Some(custom_config) = &config.customConfig else {
851         return Ok(None);
852     };
853     let Some(file) = custom_config.vendorImage.as_ref() else {
854         return Ok(None);
855     };
856 
857     let file = clone_file(file)?;
858     let size =
859         file.metadata().context("Failed to get metadata from microdroid vendor image")?.len();
860     let vbmeta = VbMetaImage::verify_reader_region(&file, 0, size)
861         .context("Failed to get vbmeta from microdroid-vendor.img")?;
862 
863     for descriptor in vbmeta.descriptors()?.iter() {
864         if let vbmeta::Descriptor::Hashtree(_) = descriptor {
865             let root_digest = hex::encode(descriptor.to_hashtree()?.root_digest());
866             return Ok(Some(root_digest.as_bytes().to_vec()));
867         }
868     }
869     Err(anyhow!("No hashtree digest is extracted from microdroid vendor image"))
870 }
871 
maybe_create_device_tree_overlay( config: &VirtualMachineConfig, temporary_directory: &Path, ) -> binder::Result<Option<File>>872 fn maybe_create_device_tree_overlay(
873     config: &VirtualMachineConfig,
874     temporary_directory: &Path,
875 ) -> binder::Result<Option<File>> {
876     // Currently, VirtMgr adds the host copy of reference DT & untrusted properties
877     // (e.g. instance-id)
878     let host_ref_dt = Path::new(VM_REFERENCE_DT_ON_HOST_PATH);
879     let host_ref_dt = if host_ref_dt.exists()
880         && read_dir(host_ref_dt).or_service_specific_exception(-1)?.next().is_some()
881     {
882         Some(host_ref_dt)
883     } else {
884         warn!("VM reference DT doesn't exist in host DT");
885         None
886     };
887 
888     let vendor_hashtree_digest = extract_vendor_hashtree_digest(config)
889         .context("Failed to extract vendor hashtree digest")
890         .or_service_specific_exception(-1)?;
891 
892     let mut trusted_props = if let Some(ref vendor_hashtree_digest) = vendor_hashtree_digest {
893         info!(
894             "Passing vendor hashtree digest to pvmfw. This will be rejected if it doesn't \
895                 match the trusted digest in the pvmfw config, causing the VM to fail to start."
896         );
897         vec![(cstr!("vendor_hashtree_descriptor_root_digest"), vendor_hashtree_digest.as_slice())]
898     } else {
899         vec![]
900     };
901 
902     let instance_id;
903     let key_material;
904     let mut untrusted_props = Vec::with_capacity(2);
905     if cfg!(llpvm_changes) {
906         instance_id = extract_instance_id(config);
907         untrusted_props.push((cstr!("instance-id"), &instance_id[..]));
908         let want_updatable = extract_want_updatable(config);
909         if want_updatable && is_secretkeeper_supported() {
910             // Let guest know that it can defer rollback protection to Secretkeeper by setting
911             // an empty property in untrusted node in DT. This enables Updatable VMs.
912             untrusted_props.push((cstr!("defer-rollback-protection"), &[]));
913             let sk: Strong<dyn ISecretkeeper> =
914                 binder::wait_for_interface(SECRETKEEPER_IDENTIFIER)?;
915             if sk.getInterfaceVersion()? >= 2 {
916                 let PublicKey { keyMaterial } = sk.getSecretkeeperIdentity()?;
917                 key_material = keyMaterial;
918                 trusted_props.push((cstr!("secretkeeper_public_key"), key_material.as_slice()));
919             }
920         }
921     }
922 
923     let device_tree_overlay = if host_ref_dt.is_some()
924         || !untrusted_props.is_empty()
925         || !trusted_props.is_empty()
926     {
927         let dt_output = temporary_directory.join(VM_DT_OVERLAY_PATH);
928         let mut data = [0_u8; VM_DT_OVERLAY_MAX_SIZE];
929         let fdt =
930             create_device_tree_overlay(&mut data, host_ref_dt, &untrusted_props, &trusted_props)
931                 .map_err(|e| anyhow!("Failed to create DT overlay, {e:?}"))
932                 .or_service_specific_exception(-1)?;
933         fs::write(&dt_output, fdt.as_slice()).or_service_specific_exception(-1)?;
934         Some(File::open(dt_output).or_service_specific_exception(-1)?)
935     } else {
936         None
937     };
938     Ok(device_tree_overlay)
939 }
940 
write_zero_filler(zero_filler_path: &Path) -> Result<()>941 fn write_zero_filler(zero_filler_path: &Path) -> Result<()> {
942     let file = OpenOptions::new()
943         .create_new(true)
944         .read(true)
945         .write(true)
946         .open(zero_filler_path)
947         .with_context(|| "Failed to create zero.img")?;
948     file.set_len(ZERO_FILLER_SIZE)?;
949     Ok(())
950 }
951 
format_as_android_vm_instance(part: &mut dyn Write) -> std::io::Result<()>952 fn format_as_android_vm_instance(part: &mut dyn Write) -> std::io::Result<()> {
953     part.write_all(ANDROID_VM_INSTANCE_MAGIC.as_bytes())?;
954     part.write_all(&ANDROID_VM_INSTANCE_VERSION.to_le_bytes())?;
955     part.flush()
956 }
957 
format_as_encryptedstore(part: &mut dyn Write) -> std::io::Result<()>958 fn format_as_encryptedstore(part: &mut dyn Write) -> std::io::Result<()> {
959     part.write_all(UNFORMATTED_STORAGE_MAGIC.as_bytes())?;
960     part.flush()
961 }
962 
round_up(input: u64, granularity: u64) -> u64963 fn round_up(input: u64, granularity: u64) -> u64 {
964     if granularity == 0 {
965         return input;
966     }
967     // If the input is absurdly large we round down instead of up; it's going to fail anyway.
968     let result = input.checked_add(granularity - 1).unwrap_or(input);
969     (result / granularity) * granularity
970 }
971 
to_input_device_option_from(input_device: &InputDevice) -> Result<InputDeviceOption>972 fn to_input_device_option_from(input_device: &InputDevice) -> Result<InputDeviceOption> {
973     Ok(match input_device {
974         InputDevice::SingleTouch(single_touch) => InputDeviceOption::SingleTouch {
975             file: clone_file(single_touch.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?)?,
976             height: u32::try_from(single_touch.height)?,
977             width: u32::try_from(single_touch.width)?,
978             name: if !single_touch.name.is_empty() {
979                 Some(single_touch.name.clone())
980             } else {
981                 None
982             },
983         },
984         InputDevice::EvDev(evdev) => InputDeviceOption::EvDev(clone_file(
985             evdev.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?,
986         )?),
987         InputDevice::Keyboard(keyboard) => InputDeviceOption::Keyboard(clone_file(
988             keyboard.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?,
989         )?),
990         InputDevice::Mouse(mouse) => InputDeviceOption::Mouse(clone_file(
991             mouse.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?,
992         )?),
993         InputDevice::Switches(switches) => InputDeviceOption::Switches(clone_file(
994             switches.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?,
995         )?),
996         InputDevice::Trackpad(trackpad) => InputDeviceOption::MultiTouchTrackpad {
997             file: clone_file(trackpad.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?)?,
998             height: u32::try_from(trackpad.height)?,
999             width: u32::try_from(trackpad.width)?,
1000             name: if !trackpad.name.is_empty() { Some(trackpad.name.clone()) } else { None },
1001         },
1002         InputDevice::MultiTouch(multi_touch) => InputDeviceOption::MultiTouch {
1003             file: clone_file(multi_touch.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?)?,
1004             height: u32::try_from(multi_touch.height)?,
1005             width: u32::try_from(multi_touch.width)?,
1006             name: if !multi_touch.name.is_empty() { Some(multi_touch.name.clone()) } else { None },
1007         },
1008     })
1009 }
1010 
assemble_shared_paths( shared_paths: &[SharedPath], temporary_directory: &Path, ) -> Result<Vec<SharedPathConfig>, Status>1011 fn assemble_shared_paths(
1012     shared_paths: &[SharedPath],
1013     temporary_directory: &Path,
1014 ) -> Result<Vec<SharedPathConfig>, Status> {
1015     if shared_paths.is_empty() {
1016         return Ok(Vec::new()); // Return an empty vector if shared_paths is empty
1017     }
1018 
1019     shared_paths
1020         .iter()
1021         .map(|path| {
1022             Ok(SharedPathConfig {
1023                 path: path.sharedPath.clone(),
1024                 host_uid: path.hostUid,
1025                 host_gid: path.hostGid,
1026                 guest_uid: path.guestUid,
1027                 guest_gid: path.guestGid,
1028                 mask: path.mask,
1029                 tag: path.tag.clone(),
1030                 socket_path: temporary_directory
1031                     .join(&path.socketPath)
1032                     .to_string_lossy()
1033                     .to_string(),
1034                 socket_fd: maybe_clone_file(&path.socketFd)?,
1035                 app_domain: path.appDomain,
1036             })
1037         })
1038         .collect()
1039 }
1040 
1041 /// Given the configuration for a disk image, assembles the `DiskFile` to pass to crosvm.
1042 ///
1043 /// This may involve assembling a composite disk from a set of partition images.
assemble_disk_image( disk: &DiskImage, zero_filler_path: &Path, temporary_directory: &Path, next_temporary_image_id: &mut u64, indirect_files: &mut Vec<File>, ) -> Result<DiskFile, Status>1044 fn assemble_disk_image(
1045     disk: &DiskImage,
1046     zero_filler_path: &Path,
1047     temporary_directory: &Path,
1048     next_temporary_image_id: &mut u64,
1049     indirect_files: &mut Vec<File>,
1050 ) -> Result<DiskFile, Status> {
1051     let image = if !disk.partitions.is_empty() {
1052         if disk.image.is_some() {
1053             warn!("DiskImage {:?} contains both image and partitions.", disk);
1054             return Err(anyhow!("DiskImage contains both image and partitions"))
1055                 .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
1056         }
1057 
1058         let composite_image_filenames =
1059             make_composite_image_filenames(temporary_directory, next_temporary_image_id);
1060         let (image, partition_files) = make_composite_image(
1061             &disk.partitions,
1062             zero_filler_path,
1063             &composite_image_filenames.composite,
1064             &composite_image_filenames.header,
1065             &composite_image_filenames.footer,
1066         )
1067         .with_context(|| format!("Failed to make composite disk image with config {:?}", disk))
1068         .with_log()
1069         .or_service_specific_exception(-1)?;
1070 
1071         // Pass the file descriptors for the various partition files to crosvm when it
1072         // is run.
1073         indirect_files.extend(partition_files);
1074 
1075         image
1076     } else if let Some(image) = &disk.image {
1077         clone_file(image)?
1078     } else {
1079         warn!("DiskImage {:?} didn't contain image or partitions.", disk);
1080         return Err(anyhow!("DiskImage didn't contain image or partitions."))
1081             .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
1082     };
1083 
1084     Ok(DiskFile { image, writable: disk.writable })
1085 }
1086 
append_kernel_param(param: &str, vm_config: &mut VirtualMachineRawConfig)1087 fn append_kernel_param(param: &str, vm_config: &mut VirtualMachineRawConfig) {
1088     if let Some(ref mut params) = vm_config.params {
1089         params.push(' ');
1090         params.push_str(param)
1091     } else {
1092         vm_config.params = Some(param.to_owned())
1093     }
1094 }
1095 
extract_os_name_from_config_path(config: &Path) -> Option<String>1096 fn extract_os_name_from_config_path(config: &Path) -> Option<String> {
1097     if config.extension()?.to_str()? != "json" {
1098         return None;
1099     }
1100 
1101     Some(config.with_extension("").file_name()?.to_str()?.to_owned())
1102 }
1103 
extract_os_names_from_configs(config_glob_pattern: &str) -> Result<HashSet<String>>1104 fn extract_os_names_from_configs(config_glob_pattern: &str) -> Result<HashSet<String>> {
1105     let configs = glob(config_glob_pattern)?.collect::<Result<Vec<_>, _>>()?;
1106     let os_names =
1107         configs.iter().filter_map(|x| extract_os_name_from_config_path(x)).collect::<HashSet<_>>();
1108 
1109     Ok(os_names)
1110 }
1111 
get_supported_os_names() -> Result<HashSet<String>>1112 fn get_supported_os_names() -> Result<HashSet<String>> {
1113     if !cfg!(vendor_modules) {
1114         return Ok(iter::once(MICRODROID_OS_NAME.to_owned()).collect());
1115     }
1116 
1117     extract_os_names_from_configs("/apex/com.android.virt/etc/microdroid*.json")
1118 }
1119 
is_valid_os(os_name: &str) -> bool1120 fn is_valid_os(os_name: &str) -> bool {
1121     SUPPORTED_OS_NAMES.contains(os_name)
1122 }
1123 
uses_gki_kernel(config: &VirtualMachineConfig) -> bool1124 fn uses_gki_kernel(config: &VirtualMachineConfig) -> bool {
1125     if !cfg!(vendor_modules) {
1126         return false;
1127     }
1128     match config {
1129         VirtualMachineConfig::RawConfig(_) => false,
1130         VirtualMachineConfig::AppConfig(config) => config.osName.starts_with("microdroid_gki-"),
1131     }
1132 }
1133 
load_app_config( config: &VirtualMachineAppConfig, debug_config: &DebugConfig, temporary_directory: &Path, ) -> Result<VirtualMachineRawConfig>1134 fn load_app_config(
1135     config: &VirtualMachineAppConfig,
1136     debug_config: &DebugConfig,
1137     temporary_directory: &Path,
1138 ) -> Result<VirtualMachineRawConfig> {
1139     let apk_file = clone_file(config.apk.as_ref().unwrap())?;
1140     let idsig_file = clone_file(config.idsig.as_ref().unwrap())?;
1141     let instance_file = clone_file(config.instanceImage.as_ref().unwrap())?;
1142 
1143     let storage_image = if let Some(file) = config.encryptedStorageImage.as_ref() {
1144         Some(clone_file(file)?)
1145     } else {
1146         None
1147     };
1148 
1149     let vm_payload_config;
1150     let extra_apk_files: Vec<_>;
1151     match &config.payload {
1152         Payload::ConfigPath(config_path) => {
1153             vm_payload_config =
1154                 load_vm_payload_config_from_file(&apk_file, config_path.as_str())
1155                     .with_context(|| format!("Couldn't read config from {}", config_path))?;
1156             extra_apk_files = vm_payload_config
1157                 .extra_apks
1158                 .iter()
1159                 .enumerate()
1160                 .map(|(i, apk)| {
1161                     File::open(PathBuf::from(&apk.path))
1162                         .with_context(|| format!("Failed to open extra apk #{i} {}", apk.path))
1163                 })
1164                 .collect::<Result<_>>()?;
1165         }
1166         Payload::PayloadConfig(payload_config) => {
1167             vm_payload_config = create_vm_payload_config(payload_config)?;
1168             extra_apk_files =
1169                 payload_config.extraApks.iter().map(clone_file).collect::<binder::Result<_>>()?;
1170         }
1171     };
1172 
1173     let payload_config_os = vm_payload_config.os.name.as_str();
1174     if !payload_config_os.is_empty() && payload_config_os != "microdroid" {
1175         bail!("'os' in payload config is deprecated");
1176     }
1177 
1178     // For now, the only supported OS is Microdroid and Microdroid GKI
1179     let os_name = config.osName.as_str();
1180     if !is_valid_os(os_name) {
1181         bail!("Unknown OS \"{}\"", os_name);
1182     }
1183 
1184     // It is safe to construct a filename based on the os_name because we've already checked that it
1185     // is one of the allowed values.
1186     let vm_config_path = PathBuf::from(format!("/apex/com.android.virt/etc/{}.json", os_name));
1187     let vm_config_file = File::open(vm_config_path)?;
1188     let mut vm_config = VmConfig::load(&vm_config_file)?.to_parcelable()?;
1189 
1190     if let Some(custom_config) = &config.customConfig {
1191         if let Some(file) = custom_config.customKernelImage.as_ref() {
1192             vm_config.kernel = Some(ParcelFileDescriptor::new(clone_file(file)?))
1193         }
1194         vm_config.gdbPort = custom_config.gdbPort;
1195 
1196         if let Some(file) = custom_config.vendorImage.as_ref() {
1197             add_microdroid_vendor_image(clone_file(file)?, &mut vm_config);
1198             append_kernel_param("androidboot.microdroid.mount_vendor=1", &mut vm_config)
1199         }
1200 
1201         vm_config.devices.clone_from(&custom_config.devices);
1202         vm_config.networkSupported = custom_config.networkSupported;
1203 
1204         for param in custom_config.extraKernelCmdlineParams.iter() {
1205             append_kernel_param(param, &mut vm_config);
1206         }
1207 
1208         vm_config.teeServices.clone_from(&custom_config.teeServices);
1209     }
1210 
1211     // Unfortunately specifying page_shift = 14 in bootconfig doesn't enable 16k pages emulation,
1212     // so we need to provide it in the kernel cmdline.
1213     // TODO(b/376901009): remove this after passing page_shift in bootconfig is supported.
1214     if os_name.ends_with("_16k") && cfg!(target_arch = "x86_64") {
1215         append_kernel_param("page_shift=14", &mut vm_config);
1216     }
1217 
1218     if config.memoryMib > 0 {
1219         vm_config.memoryMib = config.memoryMib;
1220     }
1221 
1222     vm_config.name.clone_from(&config.name);
1223     vm_config.protectedVm = config.protectedVm;
1224     vm_config.cpuTopology = config.cpuTopology;
1225     vm_config.hugePages = config.hugePages || vm_payload_config.hugepages;
1226     vm_config.boostUclamp = config.boostUclamp;
1227 
1228     // Microdroid takes additional init ramdisk & (optionally) storage image
1229     add_microdroid_system_images(config, instance_file, storage_image, os_name, &mut vm_config)?;
1230 
1231     // Include Microdroid payload disk (contains apks, idsigs) in vm config
1232     add_microdroid_payload_images(
1233         config,
1234         debug_config,
1235         temporary_directory,
1236         apk_file,
1237         idsig_file,
1238         extra_apk_files,
1239         &vm_payload_config,
1240         &mut vm_config,
1241     )?;
1242 
1243     Ok(vm_config)
1244 }
1245 
check_partition_for_file(fd: &ParcelFileDescriptor) -> Result<()>1246 fn check_partition_for_file(fd: &ParcelFileDescriptor) -> Result<()> {
1247     let path = format!("/proc/self/fd/{}", fd.as_raw_fd());
1248     let link = fs::read_link(&path).context(format!("can't read_link {path}"))?;
1249 
1250     // microdroid vendor image is OK
1251     if cfg!(vendor_modules) && link == Path::new("/vendor/etc/avf/microdroid/microdroid_vendor.img")
1252     {
1253         return Ok(());
1254     }
1255 
1256     if link.starts_with("/vendor") || link.starts_with("/odm") {
1257         bail!("vendor or odm file {} can't be used for VM", link.display());
1258     }
1259 
1260     Ok(())
1261 }
1262 
check_partitions_for_files(config: &VirtualMachineRawConfig) -> Result<()>1263 fn check_partitions_for_files(config: &VirtualMachineRawConfig) -> Result<()> {
1264     config
1265         .disks
1266         .iter()
1267         .flat_map(|disk| disk.partitions.iter())
1268         .filter_map(|partition| partition.image.as_ref())
1269         .try_for_each(check_partition_for_file)?;
1270 
1271     config.kernel.as_ref().map_or(Ok(()), check_partition_for_file)?;
1272     config.initrd.as_ref().map_or(Ok(()), check_partition_for_file)?;
1273     config.bootloader.as_ref().map_or(Ok(()), check_partition_for_file)?;
1274 
1275     Ok(())
1276 }
1277 
load_vm_payload_config_from_file(apk_file: &File, config_path: &str) -> Result<VmPayloadConfig>1278 fn load_vm_payload_config_from_file(apk_file: &File, config_path: &str) -> Result<VmPayloadConfig> {
1279     let mut apk_zip = ZipArchive::new(apk_file)?;
1280     let config_file = apk_zip.by_name(config_path)?;
1281     Ok(serde_json::from_reader(config_file)?)
1282 }
1283 
create_vm_payload_config( payload_config: &VirtualMachinePayloadConfig, ) -> Result<VmPayloadConfig>1284 fn create_vm_payload_config(
1285     payload_config: &VirtualMachinePayloadConfig,
1286 ) -> Result<VmPayloadConfig> {
1287     // There isn't an actual config file. Construct a synthetic VmPayloadConfig from the explicit
1288     // parameters we've been given. Microdroid will do something equivalent inside the VM using the
1289     // payload config that we send it via the metadata file.
1290 
1291     let payload_binary_name = &payload_config.payloadBinaryName;
1292     if payload_binary_name.contains('/') {
1293         bail!("Payload binary name must not specify a path: {payload_binary_name}");
1294     }
1295 
1296     let task = Task { type_: TaskType::MicrodroidLauncher, command: payload_binary_name.clone() };
1297 
1298     // The VM only cares about how many there are, these names are actually ignored.
1299     let extra_apk_count = payload_config.extraApks.len();
1300     let extra_apks =
1301         (0..extra_apk_count).map(|i| ApkConfig { path: format!("extra-apk-{i}") }).collect();
1302 
1303     Ok(VmPayloadConfig { task: Some(task), extra_apks, ..Default::default() })
1304 }
1305 
1306 /// Generates a unique filename to use for a composite disk image.
make_composite_image_filenames( temporary_directory: &Path, next_temporary_image_id: &mut u64, ) -> CompositeImageFilenames1307 fn make_composite_image_filenames(
1308     temporary_directory: &Path,
1309     next_temporary_image_id: &mut u64,
1310 ) -> CompositeImageFilenames {
1311     let id = *next_temporary_image_id;
1312     *next_temporary_image_id += 1;
1313     CompositeImageFilenames {
1314         composite: temporary_directory.join(format!("composite-{}.img", id)),
1315         header: temporary_directory.join(format!("composite-{}-header.img", id)),
1316         footer: temporary_directory.join(format!("composite-{}-footer.img", id)),
1317     }
1318 }
1319 
1320 /// Filenames for a composite disk image, including header and footer partitions.
1321 #[derive(Clone, Debug, Eq, PartialEq)]
1322 struct CompositeImageFilenames {
1323     /// The composite disk image itself.
1324     composite: PathBuf,
1325     /// The header partition image.
1326     header: PathBuf,
1327     /// The footer partition image.
1328     footer: PathBuf,
1329 }
1330 
1331 /// Checks whether the caller has a specific permission
check_permission(perm: &str) -> binder::Result<()>1332 fn check_permission(perm: &str) -> binder::Result<()> {
1333     if cfg!(early) {
1334         // Skip permission check for early VMs, in favor of SELinux
1335         return Ok(());
1336     }
1337     let calling_pid = get_calling_pid();
1338     let calling_uid = get_calling_uid();
1339     // Root can do anything
1340     if calling_uid == 0 {
1341         return Ok(());
1342     }
1343     let perm_svc: Strong<dyn IPermissionController::IPermissionController> =
1344         binder::wait_for_interface("permission")?;
1345     if perm_svc.checkPermission(perm, calling_pid, calling_uid as i32)? {
1346         Ok(())
1347     } else {
1348         Err(anyhow!("does not have the {} permission", perm))
1349             .or_binder_exception(ExceptionCode::SECURITY)
1350     }
1351 }
1352 
1353 /// Check whether the caller of the current Binder method is allowed to manage VMs
check_manage_access() -> binder::Result<()>1354 fn check_manage_access() -> binder::Result<()> {
1355     check_permission("android.permission.MANAGE_VIRTUAL_MACHINE")
1356 }
1357 
1358 /// Check whether the caller of the current Binder method is allowed to create custom VMs
check_use_custom_virtual_machine() -> binder::Result<()>1359 fn check_use_custom_virtual_machine() -> binder::Result<()> {
1360     check_permission("android.permission.USE_CUSTOM_VIRTUAL_MACHINE")
1361 }
1362 
1363 /// Return whether a partition is exempt from selinux label checks, because we know that it does
1364 /// not contain code and is likely to be generated in an app-writable directory.
is_safe_app_partition(label: &str) -> bool1365 fn is_safe_app_partition(label: &str) -> bool {
1366     // See add_microdroid_system_images & add_microdroid_payload_images in payload.rs.
1367     label == "vm-instance"
1368         || label == "encryptedstore"
1369         || label == "microdroid-apk-idsig"
1370         || label == "payload-metadata"
1371         || label.starts_with("extra-idsig-")
1372 }
1373 
1374 /// Returns whether a partition with the given label is safe for a raw config VM.
is_safe_raw_partition(label: &str) -> bool1375 fn is_safe_raw_partition(label: &str) -> bool {
1376     label == "vm-instance"
1377 }
1378 
1379 /// Check that a file SELinux label is acceptable.
1380 ///
1381 /// We only want to allow code in a VM to be sourced from places that apps, and the
1382 /// system or vendor, do not have write access to.
1383 ///
1384 /// Note that sepolicy must also grant read access for these types to both virtualization
1385 /// service and crosvm.
1386 ///
1387 /// App private data files are deliberately excluded, to avoid arbitrary payloads being run on
1388 /// user devices (W^X).
check_label_is_allowed(context: &SeContext) -> Result<()>1389 fn check_label_is_allowed(context: &SeContext) -> Result<()> {
1390     match context.selinux_type()? {
1391         | "apk_data_file" // APKs of an installed app
1392         | "shell_data_file" // test files created via adb shell
1393         | "staging_data_file" // updated/staged APEX images
1394         | "system_file" // immutable dm-verity protected partition
1395         | "virtualizationservice_data_file" // files created by VS / VirtMgr
1396         | "vendor_microdroid_file" // immutable dm-verity protected partition (/vendor/etc/avf/microdroid/.*)
1397          => Ok(()),
1398         _ => bail!("Label {} is not allowed", context),
1399     }
1400 }
1401 
check_label_for_partition(partition: &Partition) -> Result<()>1402 fn check_label_for_partition(partition: &Partition) -> Result<()> {
1403     let file = partition.image.as_ref().unwrap().as_ref();
1404     check_label_is_allowed(&getfilecon(file)?)
1405         .with_context(|| format!("Partition {} invalid", &partition.label))
1406 }
1407 
check_label_for_kernel_files(kernel: &Option<File>, initrd: &Option<File>) -> Result<()>1408 fn check_label_for_kernel_files(kernel: &Option<File>, initrd: &Option<File>) -> Result<()> {
1409     if let Some(f) = kernel {
1410         check_label_for_file(f, "kernel")?;
1411     }
1412     if let Some(f) = initrd {
1413         check_label_for_file(f, "initrd")?;
1414     }
1415     Ok(())
1416 }
check_label_for_file(file: &File, name: &str) -> Result<()>1417 fn check_label_for_file(file: &File, name: &str) -> Result<()> {
1418     check_label_is_allowed(&getfilecon(file)?).with_context(|| format!("{} file invalid", name))
1419 }
1420 
1421 /// Implementation of the AIDL `IVirtualMachine` interface. Used as a handle to a VM.
1422 #[derive(Debug)]
1423 struct VirtualMachine {
1424     instance: Arc<VmInstance>,
1425 }
1426 
1427 impl VirtualMachine {
create(instance: Arc<VmInstance>) -> Strong<dyn IVirtualMachine::IVirtualMachine>1428     fn create(instance: Arc<VmInstance>) -> Strong<dyn IVirtualMachine::IVirtualMachine> {
1429         BnVirtualMachine::new_binder(VirtualMachine { instance }, BinderFeatures::default())
1430     }
1431 }
1432 
1433 impl Interface for VirtualMachine {}
1434 
1435 impl IVirtualMachine::IVirtualMachine for VirtualMachine {
getCid(&self) -> binder::Result<i32>1436     fn getCid(&self) -> binder::Result<i32> {
1437         // Don't check permission. The owner of the VM might have passed this binder object to
1438         // others.
1439         Ok(self.instance.cid as i32)
1440     }
1441 
getState(&self) -> binder::Result<VirtualMachineState>1442     fn getState(&self) -> binder::Result<VirtualMachineState> {
1443         // Don't check permission. The owner of the VM might have passed this binder object to
1444         // others.
1445         Ok(get_state(&self.instance))
1446     }
1447 
registerCallback( &self, callback: &Strong<dyn IVirtualMachineCallback>, ) -> binder::Result<()>1448     fn registerCallback(
1449         &self,
1450         callback: &Strong<dyn IVirtualMachineCallback>,
1451     ) -> binder::Result<()> {
1452         // Don't check permission. The owner of the VM might have passed this binder object to
1453         // others.
1454         //
1455         // TODO: Should this give an error if the VM is already dead?
1456         self.instance.callbacks.add(callback.clone());
1457         Ok(())
1458     }
1459 
start(&self) -> binder::Result<()>1460     fn start(&self) -> binder::Result<()> {
1461         self.instance
1462             .start()
1463             .with_context(|| format!("Error starting VM with CID {}", self.instance.cid))
1464             .with_log()
1465             .or_service_specific_exception(-1)
1466     }
1467 
stop(&self) -> binder::Result<()>1468     fn stop(&self) -> binder::Result<()> {
1469         self.instance
1470             .kill()
1471             .with_context(|| format!("Error stopping VM with CID {}", self.instance.cid))
1472             .with_log()
1473             .or_service_specific_exception(-1)
1474     }
1475 
getMemoryBalloon(&self) -> binder::Result<i64>1476     fn getMemoryBalloon(&self) -> binder::Result<i64> {
1477         let balloon = self
1478             .instance
1479             .get_memory_balloon()
1480             .with_context(|| format!("Error getting balloon for VM with CID {}", self.instance.cid))
1481             .with_log()
1482             .or_service_specific_exception(-1)?;
1483         Ok(balloon.try_into().unwrap())
1484     }
1485 
setMemoryBalloon(&self, num_bytes: i64) -> binder::Result<()>1486     fn setMemoryBalloon(&self, num_bytes: i64) -> binder::Result<()> {
1487         self.instance
1488             .set_memory_balloon(num_bytes.try_into().unwrap())
1489             .with_context(|| format!("Error setting balloon for VM with CID {}", self.instance.cid))
1490             .with_log()
1491             .or_service_specific_exception(-1)
1492     }
1493 
connectVsock(&self, port: i32) -> binder::Result<ParcelFileDescriptor>1494     fn connectVsock(&self, port: i32) -> binder::Result<ParcelFileDescriptor> {
1495         if !matches!(&*self.instance.vm_state.lock().unwrap(), VmState::Running { .. }) {
1496             return Err(Status::new_service_specific_error_str(
1497                 IVirtualMachine::ERROR_UNEXPECTED,
1498                 Some("Virtual Machine is not running"),
1499             ));
1500         }
1501         let port = port as u32;
1502         if port < VSOCK_PRIV_PORT_MAX {
1503             return Err(Status::new_service_specific_error_str(
1504                 IVirtualMachine::ERROR_UNEXPECTED,
1505                 Some("Can't connect to privileged port {port}"),
1506             ));
1507         }
1508         let stream = VsockStream::connect_with_cid_port(self.instance.cid, port)
1509             .context("Failed to connect")
1510             .or_service_specific_exception(IVirtualMachine::ERROR_UNEXPECTED)?;
1511         Ok(vsock_stream_to_pfd(stream))
1512     }
1513 
createAccessorBinder(&self, name: &str, port: i32) -> binder::Result<SpIBinder>1514     fn createAccessorBinder(&self, name: &str, port: i32) -> binder::Result<SpIBinder> {
1515         if !matches!(&*self.instance.vm_state.lock().unwrap(), VmState::Running { .. }) {
1516             return Err(Status::new_service_specific_error_str(
1517                 IVirtualMachine::ERROR_UNEXPECTED,
1518                 Some("Virtual Machine is not running"),
1519             ));
1520         }
1521         let port = port as u32;
1522         if port < VSOCK_PRIV_PORT_MAX {
1523             return Err(Status::new_service_specific_error_str(
1524                 IVirtualMachine::ERROR_UNEXPECTED,
1525                 Some("Can't connect to privileged port {port}"),
1526             ));
1527         }
1528         let cid = self.instance.cid;
1529         let addr = sockaddr_vm {
1530             svm_family: AF_VSOCK as sa_family_t,
1531             svm_reserved1: 0,
1532             svm_port: port,
1533             svm_cid: cid,
1534             svm_zero: [0u8; 4],
1535         };
1536         let get_connection_info = move |_instance: &str| Some(ConnectionInfo::Vsock(addr));
1537         let accessor = Accessor::new(name, get_connection_info);
1538         accessor
1539             .as_binder()
1540             .context("The newly created Accessor should always have a binder")
1541             .or_service_specific_exception(IVirtualMachine::ERROR_UNEXPECTED)
1542     }
1543 
setHostConsoleName(&self, ptsname: &str) -> binder::Result<()>1544     fn setHostConsoleName(&self, ptsname: &str) -> binder::Result<()> {
1545         self.instance.vm_context.global_context.setHostConsoleName(ptsname)
1546     }
1547 
suspend(&self) -> binder::Result<()>1548     fn suspend(&self) -> binder::Result<()> {
1549         self.instance
1550             .suspend()
1551             .with_context(|| format!("Error suspending VM with CID {}", self.instance.cid))
1552             .with_log()
1553             .or_service_specific_exception(-1)
1554     }
1555 
resume(&self) -> binder::Result<()>1556     fn resume(&self) -> binder::Result<()> {
1557         self.instance
1558             .resume()
1559             .with_context(|| format!("Error resuming VM with CID {}", self.instance.cid))
1560             .with_log()
1561             .or_service_specific_exception(-1)
1562     }
1563 }
1564 
1565 impl Drop for VirtualMachine {
drop(&mut self)1566     fn drop(&mut self) {
1567         debug!("Dropping {:?}", self);
1568         if let Err(e) = self.instance.kill() {
1569             debug!("Error stopping dropped VM with CID {}: {:?}", self.instance.cid, e);
1570         }
1571     }
1572 }
1573 
1574 /// A set of Binders to be called back in response to various events on the VM, such as when it
1575 /// dies.
1576 #[derive(Debug, Default)]
1577 pub struct VirtualMachineCallbacks(Mutex<Vec<Strong<dyn IVirtualMachineCallback>>>);
1578 
1579 impl VirtualMachineCallbacks {
1580     /// Call all registered callbacks to notify that the payload has started.
notify_payload_started(&self, cid: Cid)1581     pub fn notify_payload_started(&self, cid: Cid) {
1582         let callbacks = &*self.0.lock().unwrap();
1583         for callback in callbacks {
1584             if let Err(e) = callback.onPayloadStarted(cid as i32) {
1585                 error!("Error notifying payload start event from VM CID {}: {:?}", cid, e);
1586             }
1587         }
1588     }
1589 
1590     /// Call all registered callbacks to notify that the payload is ready to serve.
notify_payload_ready(&self, cid: Cid)1591     pub fn notify_payload_ready(&self, cid: Cid) {
1592         let callbacks = &*self.0.lock().unwrap();
1593         for callback in callbacks {
1594             if let Err(e) = callback.onPayloadReady(cid as i32) {
1595                 error!("Error notifying payload ready event from VM CID {}: {:?}", cid, e);
1596             }
1597         }
1598     }
1599 
1600     /// Call all registered callbacks to notify that the payload has finished.
notify_payload_finished(&self, cid: Cid, exit_code: i32)1601     pub fn notify_payload_finished(&self, cid: Cid, exit_code: i32) {
1602         let callbacks = &*self.0.lock().unwrap();
1603         for callback in callbacks {
1604             if let Err(e) = callback.onPayloadFinished(cid as i32, exit_code) {
1605                 error!("Error notifying payload finish event from VM CID {}: {:?}", cid, e);
1606             }
1607         }
1608     }
1609 
1610     /// Call all registered callbacks to say that the VM encountered an error.
notify_error(&self, cid: Cid, error_code: ErrorCode, message: &str)1611     pub fn notify_error(&self, cid: Cid, error_code: ErrorCode, message: &str) {
1612         let callbacks = &*self.0.lock().unwrap();
1613         for callback in callbacks {
1614             if let Err(e) = callback.onError(cid as i32, error_code, message) {
1615                 error!("Error notifying error event from VM CID {}: {:?}", cid, e);
1616             }
1617         }
1618     }
1619 
1620     /// Call all registered callbacks to say that the VM has died.
callback_on_died(&self, cid: Cid, reason: DeathReason)1621     pub fn callback_on_died(&self, cid: Cid, reason: DeathReason) {
1622         let callbacks = &*self.0.lock().unwrap();
1623         for callback in callbacks {
1624             if let Err(e) = callback.onDied(cid as i32, reason) {
1625                 error!("Error notifying exit of VM CID {}: {:?}", cid, e);
1626             }
1627         }
1628     }
1629 
1630     /// Add a new callback to the set.
add(&self, callback: Strong<dyn IVirtualMachineCallback>)1631     fn add(&self, callback: Strong<dyn IVirtualMachineCallback>) {
1632         self.0.lock().unwrap().push(callback);
1633     }
1634 }
1635 
1636 /// The mutable state of the VirtualizationService. There should only be one instance of this
1637 /// struct.
1638 #[derive(Debug, Default)]
1639 struct State {
1640     /// The VMs which have been started. When VMs are started a weak reference is added to this
1641     /// list while a strong reference is returned to the caller over Binder. Once all copies of
1642     /// the Binder client are dropped the weak reference here will become invalid, and will be
1643     /// removed from the list opportunistically the next time `add_vm` is called.
1644     vms: Vec<Weak<VmInstance>>,
1645 }
1646 
1647 impl State {
1648     /// Get a list of VMs which still have Binder references to them.
vms(&self) -> Vec<Arc<VmInstance>>1649     fn vms(&self) -> Vec<Arc<VmInstance>> {
1650         // Attempt to upgrade the weak pointers to strong pointers.
1651         self.vms.iter().filter_map(Weak::upgrade).collect()
1652     }
1653 
1654     /// Add a new VM to the list.
add_vm(&mut self, vm: Weak<VmInstance>)1655     fn add_vm(&mut self, vm: Weak<VmInstance>) {
1656         // Garbage collect any entries from the stored list which no longer exist.
1657         self.vms.retain(|vm| vm.strong_count() > 0);
1658 
1659         // Actually add the new VM.
1660         self.vms.push(vm);
1661     }
1662 
1663     /// Get a VM that corresponds to the given cid
get_vm(&self, cid: Cid) -> Option<Arc<VmInstance>>1664     fn get_vm(&self, cid: Cid) -> Option<Arc<VmInstance>> {
1665         self.vms().into_iter().find(|vm| vm.cid == cid)
1666     }
1667 }
1668 
1669 /// Gets the `VirtualMachineState` of the given `VmInstance`.
get_state(instance: &VmInstance) -> VirtualMachineState1670 fn get_state(instance: &VmInstance) -> VirtualMachineState {
1671     match &*instance.vm_state.lock().unwrap() {
1672         VmState::NotStarted { .. } => VirtualMachineState::NOT_STARTED,
1673         VmState::Running { .. } => match instance.payload_state() {
1674             PayloadState::Starting => VirtualMachineState::STARTING,
1675             PayloadState::Started => VirtualMachineState::STARTED,
1676             PayloadState::Ready => VirtualMachineState::READY,
1677             PayloadState::Finished => VirtualMachineState::FINISHED,
1678             PayloadState::Hangup => VirtualMachineState::DEAD,
1679         },
1680         VmState::Dead => VirtualMachineState::DEAD,
1681         VmState::Failed => VirtualMachineState::DEAD,
1682     }
1683 }
1684 
1685 /// Converts a `&ParcelFileDescriptor` to a `File` by cloning the file.
clone_file(file: &ParcelFileDescriptor) -> binder::Result<File>1686 pub fn clone_file(file: &ParcelFileDescriptor) -> binder::Result<File> {
1687     file.as_ref()
1688         .try_clone()
1689         .context("Failed to clone File from ParcelFileDescriptor")
1690         .or_binder_exception(ExceptionCode::BAD_PARCELABLE)
1691         .map(File::from)
1692 }
1693 
1694 /// Converts an `&Option<ParcelFileDescriptor>` to an `Option<File>` by cloning the file.
maybe_clone_file(file: &Option<ParcelFileDescriptor>) -> binder::Result<Option<File>>1695 fn maybe_clone_file(file: &Option<ParcelFileDescriptor>) -> binder::Result<Option<File>> {
1696     file.as_ref().map(clone_file).transpose()
1697 }
1698 
1699 /// Converts a `VsockStream` to a `ParcelFileDescriptor`.
vsock_stream_to_pfd(stream: VsockStream) -> ParcelFileDescriptor1700 fn vsock_stream_to_pfd(stream: VsockStream) -> ParcelFileDescriptor {
1701     // SAFETY: ownership is transferred from stream to f
1702     let f = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
1703     ParcelFileDescriptor::new(f)
1704 }
1705 
1706 /// Parses the platform version requirement string.
parse_platform_version_req(s: &str) -> binder::Result<VersionReq>1707 fn parse_platform_version_req(s: &str) -> binder::Result<VersionReq> {
1708     VersionReq::parse(s)
1709         .with_context(|| format!("Invalid platform version requirement {}", s))
1710         .or_binder_exception(ExceptionCode::BAD_PARCELABLE)
1711 }
1712 
1713 /// Create the empty ramdump file
prepare_ramdump_file(temporary_directory: &Path) -> binder::Result<File>1714 fn prepare_ramdump_file(temporary_directory: &Path) -> binder::Result<File> {
1715     // `ramdump_write` is sent to crosvm and will be the backing store for the /dev/hvc1 where
1716     // VM will emit ramdump to. `ramdump_read` will be sent back to the client (i.e. the VM
1717     // owner) for readout.
1718     let ramdump_path = temporary_directory.join("ramdump");
1719     let ramdump = File::create(ramdump_path)
1720         .context("Failed to prepare ramdump file")
1721         .with_log()
1722         .or_service_specific_exception(-1)?;
1723     Ok(ramdump)
1724 }
1725 
1726 /// Create the empty device tree dump file
prepare_dump_dt_file(temporary_directory: &Path) -> binder::Result<File>1727 fn prepare_dump_dt_file(temporary_directory: &Path) -> binder::Result<File> {
1728     let path = temporary_directory.join("device_tree.dtb");
1729     let file = File::create(path)
1730         .context("Failed to prepare device tree dump file")
1731         .with_log()
1732         .or_service_specific_exception(-1)?;
1733     Ok(file)
1734 }
1735 
is_protected(config: &VirtualMachineConfig) -> bool1736 fn is_protected(config: &VirtualMachineConfig) -> bool {
1737     match config {
1738         VirtualMachineConfig::RawConfig(config) => config.protectedVm,
1739         VirtualMachineConfig::AppConfig(config) => config.protectedVm,
1740     }
1741 }
1742 
check_gdb_allowed(config: &VirtualMachineConfig) -> binder::Result<()>1743 fn check_gdb_allowed(config: &VirtualMachineConfig) -> binder::Result<()> {
1744     if is_protected(config) {
1745         return Err(anyhow!("Can't use gdb with protected VMs"))
1746             .or_binder_exception(ExceptionCode::SECURITY);
1747     }
1748 
1749     if get_debug_level(config) == Some(DebugLevel::NONE) {
1750         return Err(anyhow!("Can't use gdb with non-debuggable VMs"))
1751             .or_binder_exception(ExceptionCode::SECURITY);
1752     }
1753 
1754     Ok(())
1755 }
1756 
extract_instance_id(config: &VirtualMachineConfig) -> [u8; 64]1757 fn extract_instance_id(config: &VirtualMachineConfig) -> [u8; 64] {
1758     match config {
1759         VirtualMachineConfig::RawConfig(config) => config.instanceId,
1760         VirtualMachineConfig::AppConfig(config) => config.instanceId,
1761     }
1762 }
1763 
extract_want_updatable(config: &VirtualMachineConfig) -> bool1764 fn extract_want_updatable(config: &VirtualMachineConfig) -> bool {
1765     match config {
1766         VirtualMachineConfig::RawConfig(_) => true,
1767         VirtualMachineConfig::AppConfig(config) => {
1768             let Some(custom) = &config.customConfig else { return true };
1769             custom.wantUpdatable
1770         }
1771     }
1772 }
1773 
extract_gdb_port(config: &VirtualMachineConfig) -> Option<NonZeroU16>1774 fn extract_gdb_port(config: &VirtualMachineConfig) -> Option<NonZeroU16> {
1775     match config {
1776         VirtualMachineConfig::RawConfig(config) => NonZeroU16::new(config.gdbPort as u16),
1777         VirtualMachineConfig::AppConfig(config) => {
1778             NonZeroU16::new(config.customConfig.as_ref().map(|c| c.gdbPort).unwrap_or(0) as u16)
1779         }
1780     }
1781 }
1782 
check_no_vendor_modules(config: &VirtualMachineConfig) -> binder::Result<()>1783 fn check_no_vendor_modules(config: &VirtualMachineConfig) -> binder::Result<()> {
1784     let VirtualMachineConfig::AppConfig(config) = config else { return Ok(()) };
1785     if let Some(custom_config) = &config.customConfig {
1786         if custom_config.vendorImage.is_some() || custom_config.customKernelImage.is_some() {
1787             return Err(anyhow!("vendor modules feature is disabled"))
1788                 .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
1789         }
1790     }
1791     Ok(())
1792 }
1793 
check_no_devices(config: &VirtualMachineConfig) -> binder::Result<()>1794 fn check_no_devices(config: &VirtualMachineConfig) -> binder::Result<()> {
1795     let VirtualMachineConfig::AppConfig(config) = config else { return Ok(()) };
1796     if let Some(custom_config) = &config.customConfig {
1797         if !custom_config.devices.is_empty() {
1798             return Err(anyhow!("device assignment feature is disabled"))
1799                 .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
1800         }
1801     }
1802     Ok(())
1803 }
1804 
check_no_extra_apks(config: &VirtualMachineConfig) -> binder::Result<()>1805 fn check_no_extra_apks(config: &VirtualMachineConfig) -> binder::Result<()> {
1806     let VirtualMachineConfig::AppConfig(config) = config else { return Ok(()) };
1807     let Payload::PayloadConfig(payload_config) = &config.payload else { return Ok(()) };
1808     if !payload_config.extraApks.is_empty() {
1809         return Err(anyhow!("multi-tenant feature is disabled"))
1810             .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
1811     }
1812     Ok(())
1813 }
1814 
check_no_extra_kernel_cmdline_params(config: &VirtualMachineConfig) -> binder::Result<()>1815 fn check_no_extra_kernel_cmdline_params(config: &VirtualMachineConfig) -> binder::Result<()> {
1816     let VirtualMachineConfig::AppConfig(config) = config else { return Ok(()) };
1817     if let Some(custom_config) = &config.customConfig {
1818         if !custom_config.extraKernelCmdlineParams.is_empty() {
1819             return Err(anyhow!("debuggable_vms_improvements feature is disabled"))
1820                 .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
1821         }
1822     }
1823     Ok(())
1824 }
1825 
check_no_tee_services(config: &VirtualMachineConfig) -> binder::Result<()>1826 fn check_no_tee_services(config: &VirtualMachineConfig) -> binder::Result<()> {
1827     match config {
1828         VirtualMachineConfig::RawConfig(config) => {
1829             if !config.teeServices.is_empty() {
1830                 return Err(anyhow!("tee_services_allowlist feature is disabled"))
1831                     .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
1832             }
1833         }
1834         VirtualMachineConfig::AppConfig(config) => {
1835             if let Some(custom_config) = &config.customConfig {
1836                 if !custom_config.teeServices.is_empty() {
1837                     return Err(anyhow!("tee_services_allowlist feature is disabled"))
1838                         .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
1839                 }
1840             }
1841         }
1842     };
1843     Ok(())
1844 }
1845 
check_protected_vm_is_supported() -> binder::Result<()>1846 fn check_protected_vm_is_supported() -> binder::Result<()> {
1847     let is_pvm_supported =
1848         hypervisor_props::is_protected_vm_supported().or_service_specific_exception(-1)?;
1849     if is_pvm_supported {
1850         Ok(())
1851     } else {
1852         Err(anyhow!("pVM is not supported"))
1853             .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION)
1854     }
1855 }
1856 
check_config_features(config: &VirtualMachineConfig) -> binder::Result<()>1857 fn check_config_features(config: &VirtualMachineConfig) -> binder::Result<()> {
1858     if !cfg!(vendor_modules) {
1859         check_no_vendor_modules(config)?;
1860     }
1861     if !cfg!(device_assignment) {
1862         check_no_devices(config)?;
1863     }
1864     if !cfg!(multi_tenant) {
1865         check_no_extra_apks(config)?;
1866     }
1867     if !cfg!(debuggable_vms_improvements) {
1868         check_no_extra_kernel_cmdline_params(config)?;
1869     }
1870     if !cfg!(tee_services_allowlist) {
1871         check_no_tee_services(config)?;
1872     }
1873     Ok(())
1874 }
1875 
check_config_allowed_for_early_vms(config: &VirtualMachineConfig) -> binder::Result<()>1876 fn check_config_allowed_for_early_vms(config: &VirtualMachineConfig) -> binder::Result<()> {
1877     check_no_vendor_modules(config)?;
1878     check_no_devices(config)?;
1879 
1880     Ok(())
1881 }
1882 
clone_or_prepare_logger_fd( fd: Option<&ParcelFileDescriptor>, tag: String, ) -> Result<Option<File>, Status>1883 fn clone_or_prepare_logger_fd(
1884     fd: Option<&ParcelFileDescriptor>,
1885     tag: String,
1886 ) -> Result<Option<File>, Status> {
1887     if let Some(fd) = fd {
1888         return Ok(Some(clone_file(fd)?));
1889     }
1890 
1891     let (read_fd, write_fd) =
1892         pipe().context("Failed to create pipe").or_service_specific_exception(-1)?;
1893 
1894     let mut reader = BufReader::new(File::from(read_fd));
1895     let write_fd = File::from(write_fd);
1896 
1897     std::thread::spawn(move || loop {
1898         let mut buf = vec![];
1899         match reader.read_until(b'\n', &mut buf) {
1900             Ok(0) => {
1901                 // EOF
1902                 return;
1903             }
1904             Ok(size) => {
1905                 if buf[size - 1] == b'\n' {
1906                     buf.pop();
1907                 }
1908                 info!("{}: {}", &tag, &String::from_utf8_lossy(&buf));
1909             }
1910             Err(e) => {
1911                 error!("Could not read console pipe: {:?}", e);
1912                 return;
1913             }
1914         };
1915     });
1916 
1917     Ok(Some(write_fd))
1918 }
1919 
1920 /// Simple utility for referencing Borrowed or Owned. Similar to std::borrow::Cow, but
1921 /// it doesn't require that T implements Clone.
1922 enum BorrowedOrOwned<'a, T> {
1923     Borrowed(&'a T),
1924     Owned(T),
1925 }
1926 
1927 impl<'a, T> AsRef<T> for BorrowedOrOwned<'a, T> {
as_ref(&self) -> &T1928     fn as_ref(&self) -> &T {
1929         match self {
1930             Self::Borrowed(b) => b,
1931             Self::Owned(o) => o,
1932         }
1933     }
1934 }
1935 
1936 /// Implementation of `IVirtualMachineService`, the entry point of the AIDL service.
1937 #[derive(Debug, Default)]
1938 struct VirtualMachineService {
1939     state: Arc<Mutex<State>>,
1940     cid: Cid,
1941 }
1942 
1943 impl Interface for VirtualMachineService {}
1944 
1945 impl IVirtualMachineService for VirtualMachineService {
notifyPayloadStarted(&self) -> binder::Result<()>1946     fn notifyPayloadStarted(&self) -> binder::Result<()> {
1947         let cid = self.cid;
1948         if let Some(vm) = self.state.lock().unwrap().get_vm(cid) {
1949             info!("VM with CID {} started payload", cid);
1950             vm.update_payload_state(PayloadState::Started)
1951                 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)?;
1952             vm.callbacks.notify_payload_started(cid);
1953 
1954             let vm_start_timestamp = vm.vm_metric.lock().unwrap().start_timestamp;
1955             write_vm_booted_stats(vm.requester_uid as i32, &vm.name, vm_start_timestamp);
1956             Ok(())
1957         } else {
1958             error!("notifyPayloadStarted is called from an unknown CID {}", cid);
1959             Err(anyhow!("cannot find a VM with CID {}", cid)).or_service_specific_exception(-1)
1960         }
1961     }
1962 
notifyPayloadReady(&self) -> binder::Result<()>1963     fn notifyPayloadReady(&self) -> binder::Result<()> {
1964         let cid = self.cid;
1965         if let Some(vm) = self.state.lock().unwrap().get_vm(cid) {
1966             info!("VM with CID {} reported payload is ready", cid);
1967             vm.update_payload_state(PayloadState::Ready)
1968                 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)?;
1969             vm.callbacks.notify_payload_ready(cid);
1970             Ok(())
1971         } else {
1972             error!("notifyPayloadReady is called from an unknown CID {}", cid);
1973             Err(anyhow!("cannot find a VM with CID {}", cid)).or_service_specific_exception(-1)
1974         }
1975     }
1976 
notifyPayloadFinished(&self, exit_code: i32) -> binder::Result<()>1977     fn notifyPayloadFinished(&self, exit_code: i32) -> binder::Result<()> {
1978         let cid = self.cid;
1979         if let Some(vm) = self.state.lock().unwrap().get_vm(cid) {
1980             info!("VM with CID {} finished payload", cid);
1981             vm.update_payload_state(PayloadState::Finished)
1982                 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)?;
1983             vm.callbacks.notify_payload_finished(cid, exit_code);
1984             Ok(())
1985         } else {
1986             error!("notifyPayloadFinished is called from an unknown CID {}", cid);
1987             Err(anyhow!("cannot find a VM with CID {}", cid)).or_service_specific_exception(-1)
1988         }
1989     }
1990 
notifyError(&self, error_code: ErrorCode, message: &str) -> binder::Result<()>1991     fn notifyError(&self, error_code: ErrorCode, message: &str) -> binder::Result<()> {
1992         let cid = self.cid;
1993         if let Some(vm) = self.state.lock().unwrap().get_vm(cid) {
1994             info!("VM with CID {} encountered an error", cid);
1995             vm.update_payload_state(PayloadState::Finished)
1996                 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)?;
1997             vm.callbacks.notify_error(cid, error_code, message);
1998             Ok(())
1999         } else {
2000             error!("notifyError is called from an unknown CID {}", cid);
2001             Err(anyhow!("cannot find a VM with CID {}", cid)).or_service_specific_exception(-1)
2002         }
2003     }
2004 
getSecretkeeper(&self) -> binder::Result<Strong<dyn ISecretkeeper>>2005     fn getSecretkeeper(&self) -> binder::Result<Strong<dyn ISecretkeeper>> {
2006         if !is_secretkeeper_supported() {
2007             return Err(StatusCode::NAME_NOT_FOUND)?;
2008         }
2009         let sk = binder::wait_for_interface(SECRETKEEPER_IDENTIFIER)?;
2010         Ok(BnSecretkeeper::new_binder(SecretkeeperProxy(sk), BinderFeatures::default()))
2011     }
2012 
requestAttestation(&self, csr: &[u8], test_mode: bool) -> binder::Result<Vec<Certificate>>2013     fn requestAttestation(&self, csr: &[u8], test_mode: bool) -> binder::Result<Vec<Certificate>> {
2014         GLOBAL_SERVICE.requestAttestation(csr, get_calling_uid() as i32, test_mode)
2015     }
2016 }
2017 
is_secretkeeper_supported() -> bool2018 fn is_secretkeeper_supported() -> bool {
2019     binder::is_declared(SECRETKEEPER_IDENTIFIER)
2020         .expect("Could not check for declared Secretkeeper interface")
2021 }
2022 
2023 impl VirtualMachineService {
new_binder(state: Arc<Mutex<State>>, cid: Cid) -> Strong<dyn IVirtualMachineService>2024     fn new_binder(state: Arc<Mutex<State>>, cid: Cid) -> Strong<dyn IVirtualMachineService> {
2025         BnVirtualMachineService::new_binder(
2026             VirtualMachineService { state, cid },
2027             BinderFeatures::default(),
2028         )
2029     }
2030 }
2031 
2032 struct SecretkeeperProxy(Strong<dyn ISecretkeeper>);
2033 
2034 impl Interface for SecretkeeperProxy {}
2035 
2036 impl ISecretkeeper for SecretkeeperProxy {
processSecretManagementRequest(&self, req: &[u8]) -> binder::Result<Vec<u8>>2037     fn processSecretManagementRequest(&self, req: &[u8]) -> binder::Result<Vec<u8>> {
2038         // Pass the request to the channel, and read the response.
2039         self.0.processSecretManagementRequest(req)
2040     }
2041 
getAuthGraphKe(&self) -> binder::Result<Strong<dyn IAuthGraphKeyExchange>>2042     fn getAuthGraphKe(&self) -> binder::Result<Strong<dyn IAuthGraphKeyExchange>> {
2043         let ag = AuthGraphKeyExchangeProxy(self.0.getAuthGraphKe()?);
2044         Ok(BnAuthGraphKeyExchange::new_binder(ag, BinderFeatures::default()))
2045     }
2046 
deleteIds(&self, ids: &[SecretId]) -> binder::Result<()>2047     fn deleteIds(&self, ids: &[SecretId]) -> binder::Result<()> {
2048         self.0.deleteIds(ids)
2049     }
2050 
deleteAll(&self) -> binder::Result<()>2051     fn deleteAll(&self) -> binder::Result<()> {
2052         self.0.deleteAll()
2053     }
2054 
getSecretkeeperIdentity(&self) -> binder::Result<PublicKey>2055     fn getSecretkeeperIdentity(&self) -> binder::Result<PublicKey> {
2056         // SecretkeeperProxy is really a RPC binder service for PVM (It is called by
2057         // MicrodroidManager). PVMs do not & must not (for security reason) rely on
2058         // getSecretKeeperIdentity, so we throw an exception if someone attempts to
2059         // use this API from the proxy.
2060         Err(ExceptionCode::SECURITY.into())
2061     }
2062 }
2063 
2064 struct AuthGraphKeyExchangeProxy(Strong<dyn IAuthGraphKeyExchange>);
2065 
2066 impl Interface for AuthGraphKeyExchangeProxy {}
2067 
2068 impl IAuthGraphKeyExchange for AuthGraphKeyExchangeProxy {
create(&self) -> binder::Result<SessionInitiationInfo>2069     fn create(&self) -> binder::Result<SessionInitiationInfo> {
2070         self.0.create()
2071     }
2072 
init( &self, peer_pub_key: &PubKey, peer_id: &Identity, peer_nonce: &[u8], peer_version: i32, ) -> binder::Result<KeInitResult>2073     fn init(
2074         &self,
2075         peer_pub_key: &PubKey,
2076         peer_id: &Identity,
2077         peer_nonce: &[u8],
2078         peer_version: i32,
2079     ) -> binder::Result<KeInitResult> {
2080         self.0.init(peer_pub_key, peer_id, peer_nonce, peer_version)
2081     }
2082 
finish( &self, peer_pub_key: &PubKey, peer_id: &Identity, peer_signature: &SessionIdSignature, peer_nonce: &[u8], peer_version: i32, own_key: &Key, ) -> binder::Result<SessionInfo>2083     fn finish(
2084         &self,
2085         peer_pub_key: &PubKey,
2086         peer_id: &Identity,
2087         peer_signature: &SessionIdSignature,
2088         peer_nonce: &[u8],
2089         peer_version: i32,
2090         own_key: &Key,
2091     ) -> binder::Result<SessionInfo> {
2092         self.0.finish(peer_pub_key, peer_id, peer_signature, peer_nonce, peer_version, own_key)
2093     }
2094 
authenticationComplete( &self, peer_signature: &SessionIdSignature, shared_keys: &[AuthgraphArc; 2], ) -> binder::Result<[AuthgraphArc; 2]>2095     fn authenticationComplete(
2096         &self,
2097         peer_signature: &SessionIdSignature,
2098         shared_keys: &[AuthgraphArc; 2],
2099     ) -> binder::Result<[AuthgraphArc; 2]> {
2100         self.0.authenticationComplete(peer_signature, shared_keys)
2101     }
2102 }
2103 
2104 // KEEP IN SYNC WITH early_vms.xsd
2105 #[derive(Clone, Debug, Deserialize, PartialEq)]
2106 struct EarlyVm {
2107     name: String,
2108     cid: i32,
2109     path: String,
2110 }
2111 
2112 #[derive(Debug, Default, Deserialize)]
2113 struct EarlyVms {
2114     early_vm: Vec<EarlyVm>,
2115 }
2116 
2117 static EARLY_VMS_CACHE: LazyLock<Mutex<HashMap<String, Vec<EarlyVm>>>> =
2118     LazyLock::new(|| Mutex::new(HashMap::new()));
2119 
range_for_partition(partition: &str) -> Result<Range<Cid>>2120 fn range_for_partition(partition: &str) -> Result<Range<Cid>> {
2121     match partition {
2122         "system" => Ok(100..200),
2123         "system_ext" | "product" => Ok(200..300),
2124         _ => Err(anyhow!("Early VMs are not supported for {partition}")),
2125     }
2126 }
2127 
get_early_vms_in_path(xml_path: &Path) -> Result<Vec<EarlyVm>>2128 fn get_early_vms_in_path(xml_path: &Path) -> Result<Vec<EarlyVm>> {
2129     if !xml_path.exists() {
2130         bail!("{} doesn't exist", xml_path.display());
2131     }
2132 
2133     let xml =
2134         fs::read(xml_path).with_context(|| format!("Failed to read {}", xml_path.display()))?;
2135     let xml = String::from_utf8(xml)
2136         .with_context(|| format!("{} is not a valid UTF-8 file", xml_path.display()))?;
2137     let early_vms: EarlyVms = serde_xml_rs::from_str(&xml)
2138         .with_context(|| format!("Can't parse {}", xml_path.display()))?;
2139 
2140     Ok(early_vms.early_vm)
2141 }
2142 
validate_cid_range(early_vms: &[EarlyVm], cid_range: &Range<Cid>) -> Result<()>2143 fn validate_cid_range(early_vms: &[EarlyVm], cid_range: &Range<Cid>) -> Result<()> {
2144     for early_vm in early_vms {
2145         let cid = early_vm
2146             .cid
2147             .try_into()
2148             .with_context(|| format!("VM '{}' uses Invalid CID {}", early_vm.name, early_vm.cid))?;
2149 
2150         ensure!(
2151             cid_range.contains(&cid),
2152             "VM '{}' uses CID {cid} which is out of range. Available CIDs: {cid_range:?}",
2153             early_vm.name
2154         );
2155     }
2156     Ok(())
2157 }
2158 
get_early_vms_in_partition(partition: &str) -> Result<Vec<EarlyVm>>2159 fn get_early_vms_in_partition(partition: &str) -> Result<Vec<EarlyVm>> {
2160     let mut cache = EARLY_VMS_CACHE.lock().unwrap();
2161 
2162     if let Some(result) = cache.get(partition) {
2163         return Ok(result.clone());
2164     }
2165 
2166     let pattern = format!("/{partition}/etc/avf/early_vms*.xml");
2167     let mut early_vms = Vec::new();
2168     for entry in glob::glob(&pattern).with_context(|| format!("Failed to glob {}", &pattern))? {
2169         match entry {
2170             Ok(path) => early_vms.extend(get_early_vms_in_path(&path)?),
2171             Err(e) => error!("Error while globbing (but continuing) {}: {}", &pattern, e),
2172         }
2173     }
2174 
2175     validate_cid_range(&early_vms, &range_for_partition(partition)?)
2176         .with_context(|| format!("CID validation for {partition} failed"))?;
2177 
2178     cache.insert(partition.to_owned(), early_vms.clone());
2179 
2180     Ok(early_vms)
2181 }
2182 
find_early_vm<'a>(early_vms: &'a [EarlyVm], name: &str) -> Result<&'a EarlyVm>2183 fn find_early_vm<'a>(early_vms: &'a [EarlyVm], name: &str) -> Result<&'a EarlyVm> {
2184     let mut found_vm: Option<&EarlyVm> = None;
2185 
2186     for early_vm in early_vms {
2187         if early_vm.name != name {
2188             continue;
2189         }
2190 
2191         if found_vm.is_some() {
2192             bail!("Multiple VMs named '{name}' are found");
2193         }
2194 
2195         found_vm = Some(early_vm);
2196     }
2197 
2198     found_vm.ok_or_else(|| anyhow!("Can't find a VM named '{name}'"))
2199 }
2200 
find_early_vm_for_partition(partition: &str, name: &str) -> Result<EarlyVm>2201 fn find_early_vm_for_partition(partition: &str, name: &str) -> Result<EarlyVm> {
2202     let early_vms = get_early_vms_in_partition(partition)
2203         .with_context(|| format!("Failed to get early VMs from {partition}"))?;
2204 
2205     Ok(find_early_vm(&early_vms, name)
2206         .with_context(|| format!("Failed to find early VM '{name}' in {partition}"))?
2207         .clone())
2208 }
2209 
2210 #[cfg(test)]
2211 mod tests {
2212     use super::*;
2213 
2214     #[test]
test_is_allowed_label_for_partition() -> Result<()>2215     fn test_is_allowed_label_for_partition() -> Result<()> {
2216         let expected_results = vec![
2217             ("u:object_r:system_file:s0", true),
2218             ("u:object_r:apk_data_file:s0", true),
2219             ("u:object_r:app_data_file:s0", false),
2220             ("u:object_r:app_data_file:s0:c512,c768", false),
2221             ("u:object_r:privapp_data_file:s0:c512,c768", false),
2222             ("invalid", false),
2223             ("user:role:apk_data_file:severity:categories", true),
2224             ("user:role:apk_data_file:severity:categories:extraneous", false),
2225         ];
2226 
2227         for (label, expected_valid) in expected_results {
2228             let context = SeContext::new(label)?;
2229             let result = check_label_is_allowed(&context);
2230             if expected_valid {
2231                 assert!(result.is_ok(), "Expected label {} to be allowed, got {:?}", label, result);
2232             } else if result.is_ok() {
2233                 bail!("Expected label {} to be disallowed", label);
2234             }
2235         }
2236         Ok(())
2237     }
2238 
2239     #[test]
test_create_or_update_idsig_file_empty_apk() -> Result<()>2240     fn test_create_or_update_idsig_file_empty_apk() -> Result<()> {
2241         let apk = tempfile::tempfile().unwrap();
2242         let idsig = tempfile::tempfile().unwrap();
2243 
2244         let ret = create_or_update_idsig_file(
2245             &ParcelFileDescriptor::new(apk),
2246             &ParcelFileDescriptor::new(idsig),
2247         );
2248         assert!(ret.is_err(), "should fail");
2249         Ok(())
2250     }
2251 
2252     #[test]
test_create_or_update_idsig_dir_instead_of_file_for_apk() -> Result<()>2253     fn test_create_or_update_idsig_dir_instead_of_file_for_apk() -> Result<()> {
2254         let tmp_dir = tempfile::TempDir::new().unwrap();
2255         let apk = File::open(tmp_dir.path()).unwrap();
2256         let idsig = tempfile::tempfile().unwrap();
2257 
2258         let ret = create_or_update_idsig_file(
2259             &ParcelFileDescriptor::new(apk),
2260             &ParcelFileDescriptor::new(idsig),
2261         );
2262         assert!(ret.is_err(), "should fail");
2263         Ok(())
2264     }
2265 
2266     /// Verifies that create_or_update_idsig_file won't oom if a fd that corresponds to a directory
2267     /// on ext4 filesystem is passed.
2268     /// On ext4 lseek on a directory fd will return (off_t)-1 (see:
2269     /// https://bugzilla.kernel.org/show_bug.cgi?id=200043), which will result in
2270     /// create_or_update_idsig_file ooming while attempting to allocate petabytes of memory.
2271     #[test]
test_create_or_update_idsig_does_not_crash_dir_on_ext4() -> Result<()>2272     fn test_create_or_update_idsig_does_not_crash_dir_on_ext4() -> Result<()> {
2273         // APEXes are backed by the ext4.
2274         let apk = File::open("/apex/com.android.virt/").unwrap();
2275         let idsig = tempfile::tempfile().unwrap();
2276 
2277         let ret = create_or_update_idsig_file(
2278             &ParcelFileDescriptor::new(apk),
2279             &ParcelFileDescriptor::new(idsig),
2280         );
2281         assert!(ret.is_err(), "should fail");
2282         Ok(())
2283     }
2284 
2285     #[test]
test_create_or_update_idsig_does_not_update_if_already_valid() -> Result<()>2286     fn test_create_or_update_idsig_does_not_update_if_already_valid() -> Result<()> {
2287         use std::io::Seek;
2288 
2289         // Pick any APK
2290         let mut apk = File::open("/system/priv-app/Shell/Shell.apk").unwrap();
2291         let mut idsig = tempfile::tempfile().unwrap();
2292 
2293         create_or_update_idsig_file(
2294             &ParcelFileDescriptor::new(apk.try_clone()?),
2295             &ParcelFileDescriptor::new(idsig.try_clone()?),
2296         )?;
2297         let modified_orig = idsig.metadata()?.modified()?;
2298         apk.rewind()?;
2299         idsig.rewind()?;
2300 
2301         // Call the function again
2302         create_or_update_idsig_file(
2303             &ParcelFileDescriptor::new(apk.try_clone()?),
2304             &ParcelFileDescriptor::new(idsig.try_clone()?),
2305         )?;
2306         let modified_new = idsig.metadata()?.modified()?;
2307         assert!(modified_orig == modified_new, "idsig file was updated unnecessarily");
2308         Ok(())
2309     }
2310 
2311     #[test]
test_create_or_update_idsig_on_non_empty_file() -> Result<()>2312     fn test_create_or_update_idsig_on_non_empty_file() -> Result<()> {
2313         use std::io::Read;
2314 
2315         // Pick any APK
2316         let mut apk = File::open("/system/priv-app/Shell/Shell.apk").unwrap();
2317         let idsig_empty = tempfile::tempfile().unwrap();
2318         let mut idsig_invalid = tempfile::tempfile().unwrap();
2319         idsig_invalid.write_all(b"Oops")?;
2320 
2321         // Create new idsig
2322         create_or_update_idsig_file(
2323             &ParcelFileDescriptor::new(apk.try_clone()?),
2324             &ParcelFileDescriptor::new(idsig_empty.try_clone()?),
2325         )?;
2326         apk.rewind()?;
2327 
2328         // Update idsig_invalid
2329         create_or_update_idsig_file(
2330             &ParcelFileDescriptor::new(apk.try_clone()?),
2331             &ParcelFileDescriptor::new(idsig_invalid.try_clone()?),
2332         )?;
2333 
2334         // Ensure the 2 idsig files have same size!
2335         assert!(
2336             idsig_empty.metadata()?.len() == idsig_invalid.metadata()?.len(),
2337             "idsig files differ in size"
2338         );
2339         // Ensure the 2 idsig files have same content!
2340         for (b1, b2) in idsig_empty.bytes().zip(idsig_invalid.bytes()) {
2341             assert!(b1.unwrap() == b2.unwrap(), "idsig files differ")
2342         }
2343         Ok(())
2344     }
2345     #[test]
test_append_kernel_param_first_param()2346     fn test_append_kernel_param_first_param() {
2347         let mut vm_config = VirtualMachineRawConfig { ..Default::default() };
2348         append_kernel_param("foo=1", &mut vm_config);
2349         assert_eq!(vm_config.params, Some("foo=1".to_owned()))
2350     }
2351 
2352     #[test]
test_append_kernel_param()2353     fn test_append_kernel_param() {
2354         let mut vm_config =
2355             VirtualMachineRawConfig { params: Some("foo=5".to_owned()), ..Default::default() };
2356         append_kernel_param("bar=42", &mut vm_config);
2357         assert_eq!(vm_config.params, Some("foo=5 bar=42".to_owned()))
2358     }
2359 
test_extract_os_name_from_config_path( path: &Path, expected_result: Option<&str>, ) -> Result<()>2360     fn test_extract_os_name_from_config_path(
2361         path: &Path,
2362         expected_result: Option<&str>,
2363     ) -> Result<()> {
2364         let result = extract_os_name_from_config_path(path);
2365         if result.as_deref() != expected_result {
2366             bail!("Expected {:?} but was {:?}", expected_result, &result)
2367         }
2368         Ok(())
2369     }
2370 
2371     #[test]
test_extract_os_name_from_microdroid_config() -> Result<()>2372     fn test_extract_os_name_from_microdroid_config() -> Result<()> {
2373         test_extract_os_name_from_config_path(
2374             Path::new("/apex/com.android.virt/etc/microdroid.json"),
2375             Some("microdroid"),
2376         )
2377     }
2378 
2379     #[test]
test_extract_os_name_from_microdroid_16k_config() -> Result<()>2380     fn test_extract_os_name_from_microdroid_16k_config() -> Result<()> {
2381         test_extract_os_name_from_config_path(
2382             Path::new("/apex/com.android.virt/etc/microdroid_16k.json"),
2383             Some("microdroid_16k"),
2384         )
2385     }
2386 
2387     #[test]
test_extract_os_name_from_microdroid_gki_config() -> Result<()>2388     fn test_extract_os_name_from_microdroid_gki_config() -> Result<()> {
2389         test_extract_os_name_from_config_path(
2390             Path::new("/apex/com.android.virt/etc/microdroid_gki-android14-6.1.json"),
2391             Some("microdroid_gki-android14-6.1"),
2392         )
2393     }
2394 
2395     #[test]
test_extract_os_name_from_invalid_path() -> Result<()>2396     fn test_extract_os_name_from_invalid_path() -> Result<()> {
2397         test_extract_os_name_from_config_path(
2398             Path::new("/apex/com.android.virt/etc/microdroid.img"),
2399             None,
2400         )
2401     }
2402 
2403     #[test]
test_extract_os_name_from_configs() -> Result<()>2404     fn test_extract_os_name_from_configs() -> Result<()> {
2405         let tmp_dir = tempfile::TempDir::new()?;
2406         let tmp_dir_path = tmp_dir.path().to_owned();
2407 
2408         let mut os_names: HashSet<String> = HashSet::new();
2409         os_names.insert("microdroid".to_owned());
2410         os_names.insert("microdroid_gki-android14-6.1".to_owned());
2411         os_names.insert("microdroid_gki-android15-6.1".to_owned());
2412 
2413         // config files
2414         for os_name in &os_names {
2415             std::fs::write(tmp_dir_path.join(os_name.to_owned() + ".json"), b"")?;
2416         }
2417 
2418         // fake files not related to configs
2419         std::fs::write(tmp_dir_path.join("microdroid_super.img"), b"")?;
2420         std::fs::write(tmp_dir_path.join("microdroid_foobar.apk"), b"")?;
2421 
2422         let glob_pattern = match tmp_dir_path.join("microdroid*.json").to_str() {
2423             Some(s) => s.to_owned(),
2424             None => bail!("tmp_dir_path {:?} is not UTF-8", tmp_dir_path),
2425         };
2426 
2427         let result = extract_os_names_from_configs(&glob_pattern)?;
2428         if result != os_names {
2429             bail!("Expected {:?} but was {:?}", os_names, result);
2430         }
2431         Ok(())
2432     }
2433 
2434     #[test]
test_find_early_vms_from_xml() -> Result<()>2435     fn test_find_early_vms_from_xml() -> Result<()> {
2436         let tmp_dir = tempfile::TempDir::new()?;
2437         let tmp_dir_path = tmp_dir.path().to_owned();
2438         let xml_path = tmp_dir_path.join("early_vms.xml");
2439 
2440         std::fs::write(
2441             &xml_path,
2442             br#"<?xml version="1.0" encoding="utf-8"?>
2443         <early_vms>
2444             <early_vm>
2445                 <name>vm_demo_native_early</name>
2446                 <cid>123</cid>
2447                 <path>/system/bin/vm_demo_native_early</path>
2448             </early_vm>
2449             <early_vm>
2450                 <name>vm_demo_native_early_2</name>
2451                 <cid>456</cid>
2452                 <path>/system/bin/vm_demo_native_early_2</path>
2453             </early_vm>
2454         </early_vms>
2455         "#,
2456         )?;
2457 
2458         let cid_range = 100..1000;
2459 
2460         let early_vms = get_early_vms_in_path(&xml_path)?;
2461         validate_cid_range(&early_vms, &cid_range)?;
2462 
2463         let test_cases = [
2464             (
2465                 "vm_demo_native_early",
2466                 EarlyVm {
2467                     name: "vm_demo_native_early".to_owned(),
2468                     cid: 123,
2469                     path: "/system/bin/vm_demo_native_early".to_owned(),
2470                 },
2471             ),
2472             (
2473                 "vm_demo_native_early_2",
2474                 EarlyVm {
2475                     name: "vm_demo_native_early_2".to_owned(),
2476                     cid: 456,
2477                     path: "/system/bin/vm_demo_native_early_2".to_owned(),
2478                 },
2479             ),
2480         ];
2481 
2482         for (name, expected) in test_cases {
2483             let result = find_early_vm(&early_vms, name)?;
2484             assert_eq!(result, &expected);
2485         }
2486 
2487         Ok(())
2488     }
2489 
2490     #[test]
test_invalid_cid_validation() -> Result<()>2491     fn test_invalid_cid_validation() -> Result<()> {
2492         let tmp_dir = tempfile::TempDir::new()?;
2493         let xml_path = tmp_dir.path().join("early_vms.xml");
2494 
2495         let cid_range = 100..1000;
2496 
2497         for cid in [-1, 999999] {
2498             std::fs::write(
2499                 &xml_path,
2500                 format!(
2501                     r#"<?xml version="1.0" encoding="utf-8"?>
2502         <early_vms>
2503             <early_vm>
2504                 <name>vm_demo_invalid_cid</name>
2505                 <cid>{cid}</cid>
2506                 <path>/system/bin/vm_demo_invalid_cid</path>
2507             </early_vm>
2508         </early_vms>
2509         "#
2510                 ),
2511             )?;
2512 
2513             let early_vms = get_early_vms_in_path(&xml_path)?;
2514             assert!(validate_cid_range(&early_vms, &cid_range).is_err(), "should fail");
2515         }
2516 
2517         Ok(())
2518     }
2519 
2520     #[test]
test_duplicated_early_vms() -> Result<()>2521     fn test_duplicated_early_vms() -> Result<()> {
2522         let tmp_dir = tempfile::TempDir::new()?;
2523         let tmp_dir_path = tmp_dir.path().to_owned();
2524         let xml_path = tmp_dir_path.join("early_vms.xml");
2525 
2526         std::fs::write(
2527             &xml_path,
2528             br#"<?xml version="1.0" encoding="utf-8"?>
2529         <early_vms>
2530             <early_vm>
2531                 <name>vm_demo_duplicated_name</name>
2532                 <cid>456</cid>
2533                 <path>/system/bin/vm_demo_duplicated_name_1</path>
2534             </early_vm>
2535             <early_vm>
2536                 <name>vm_demo_duplicated_name</name>
2537                 <cid>789</cid>
2538                 <path>/system/bin/vm_demo_duplicated_name_2</path>
2539             </early_vm>
2540         </early_vms>
2541         "#,
2542         )?;
2543 
2544         let cid_range = 100..1000;
2545 
2546         let early_vms = get_early_vms_in_path(&xml_path)?;
2547         validate_cid_range(&early_vms, &cid_range)?;
2548 
2549         assert!(find_early_vm(&early_vms, "vm_demo_duplicated_name").is_err(), "should fail");
2550 
2551         Ok(())
2552     }
2553 }
2554