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::{CrosvmConfig, DiskFile, DisplayConfig, GpuConfig, InputDeviceOption, PayloadState, 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::{getfilecon, 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     InputDevice::InputDevice,
36     IVirtualMachine::{BnVirtualMachine, IVirtualMachine},
37     IVirtualMachineCallback::IVirtualMachineCallback,
38     IVirtualizationService::IVirtualizationService,
39     MemoryTrimLevel::MemoryTrimLevel,
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::IVirtualizationServiceInternal::IVirtualizationServiceInternal;
50 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
51         BnVirtualMachineService, IVirtualMachineService,
52 };
53 use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::ISecretkeeper::{BnSecretkeeper, ISecretkeeper};
54 use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::SecretId::SecretId;
55 use android_hardware_security_authgraph::aidl::android::hardware::security::authgraph::{
56     Arc::Arc as AuthgraphArc, IAuthGraphKeyExchange::IAuthGraphKeyExchange,
57     IAuthGraphKeyExchange::BnAuthGraphKeyExchange, Identity::Identity, KeInitResult::KeInitResult,
58     Key::Key, PubKey::PubKey, SessionIdSignature::SessionIdSignature, SessionInfo::SessionInfo,
59     SessionInitiationInfo::SessionInitiationInfo,
60 };
61 use anyhow::{anyhow, bail, Context, Result};
62 use apkverify::{HashAlgorithm, V4Signature};
63 use avflog::LogResult;
64 use binder::{
65     self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, ParcelFileDescriptor,
66     Status, StatusCode, Strong,
67     IntoBinderResult,
68 };
69 use cstr::cstr;
70 use glob::glob;
71 use lazy_static::lazy_static;
72 use log::{debug, error, info, warn};
73 use microdroid_payload_config::{ApkConfig, Task, TaskType, VmPayloadConfig};
74 use nix::unistd::pipe;
75 use rpcbinder::RpcServer;
76 use rustutils::system_properties;
77 use semver::VersionReq;
78 use std::collections::HashSet;
79 use std::convert::TryInto;
80 use std::fs;
81 use std::ffi::CStr;
82 use std::fs::{canonicalize, read_dir, remove_file, File, OpenOptions};
83 use std::io::{BufRead, BufReader, Error, ErrorKind, Seek, SeekFrom, Write};
84 use std::iter;
85 use std::num::{NonZeroU16, NonZeroU32};
86 use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
87 use std::os::unix::raw::pid_t;
88 use std::path::{Path, PathBuf};
89 use std::sync::{Arc, Mutex, Weak};
90 use vbmeta::VbMetaImage;
91 use vmconfig::{VmConfig, get_debug_level};
92 use vsock::VsockStream;
93 use zip::ZipArchive;
94 
95 /// The unique ID of a VM used (together with a port number) for vsock communication.
96 pub type Cid = u32;
97 
98 pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
99 
100 /// The size of zero.img.
101 /// Gaps in composite disk images are filled with a shared zero.img.
102 const ZERO_FILLER_SIZE: u64 = 4096;
103 
104 /// Magic string for the instance image
105 const ANDROID_VM_INSTANCE_MAGIC: &str = "Android-VM-instance";
106 
107 /// Version of the instance image format
108 const ANDROID_VM_INSTANCE_VERSION: u16 = 1;
109 
110 const MICRODROID_OS_NAME: &str = "microdroid";
111 
112 const SECRETKEEPER_IDENTIFIER: &str =
113     "android.hardware.security.secretkeeper.ISecretkeeper/default";
114 
115 const UNFORMATTED_STORAGE_MAGIC: &str = "UNFORMATTED-STORAGE";
116 
117 /// crosvm requires all partitions to be a multiple of 4KiB.
118 const PARTITION_GRANULARITY_BYTES: u64 = 4096;
119 
120 const VM_REFERENCE_DT_ON_HOST_PATH: &str = "/proc/device-tree/avf/reference";
121 
122 lazy_static! {
123     pub static ref GLOBAL_SERVICE: Strong<dyn IVirtualizationServiceInternal> =
124         wait_for_interface(BINDER_SERVICE_IDENTIFIER)
125             .expect("Could not connect to VirtualizationServiceInternal");
126     static ref SUPPORTED_OS_NAMES: HashSet<String> =
127         get_supported_os_names().expect("Failed to get list of supported os names");
128 }
129 
create_or_update_idsig_file( input_fd: &ParcelFileDescriptor, idsig_fd: &ParcelFileDescriptor, ) -> Result<()>130 fn create_or_update_idsig_file(
131     input_fd: &ParcelFileDescriptor,
132     idsig_fd: &ParcelFileDescriptor,
133 ) -> Result<()> {
134     let mut input = clone_file(input_fd)?;
135     let metadata = input.metadata().context("failed to get input metadata")?;
136     if !metadata.is_file() {
137         bail!("input is not a regular file");
138     }
139     let mut sig =
140         V4Signature::create(&mut input, get_current_sdk()?, 4096, &[], HashAlgorithm::SHA256)
141             .context("failed to create idsig")?;
142 
143     let mut output = clone_file(idsig_fd)?;
144 
145     // Optimization. We don't have to update idsig file whenever a VM is started. Don't update it,
146     // if the idsig file already has the same APK digest.
147     if output.metadata()?.len() > 0 {
148         if let Ok(out_sig) = V4Signature::from_idsig(&mut output) {
149             if out_sig.signing_info.apk_digest == sig.signing_info.apk_digest {
150                 debug!("idsig {:?} is up-to-date with apk {:?}.", output, input);
151                 return Ok(());
152             }
153         }
154         // if we fail to read v4signature from output, that's fine. User can pass a random file.
155         // We will anyway overwrite the file to the v4signature generated from input_fd.
156     }
157 
158     output
159         .seek(SeekFrom::Start(0))
160         .context("failed to move cursor to start on the idsig output")?;
161     output.set_len(0).context("failed to set_len on the idsig output")?;
162     sig.write_into(&mut output).context("failed to write idsig")?;
163     Ok(())
164 }
165 
get_current_sdk() -> Result<u32>166 fn get_current_sdk() -> Result<u32> {
167     let current_sdk = system_properties::read("ro.build.version.sdk")?;
168     let current_sdk = current_sdk.ok_or_else(|| anyhow!("SDK version missing"))?;
169     current_sdk.parse().context("Malformed SDK version")
170 }
171 
remove_temporary_files(path: &PathBuf) -> Result<()>172 pub fn remove_temporary_files(path: &PathBuf) -> Result<()> {
173     for dir_entry in read_dir(path)? {
174         remove_file(dir_entry?.path())?;
175     }
176     Ok(())
177 }
178 
179 /// Implementation of `IVirtualizationService`, the entry point of the AIDL service.
180 #[derive(Debug, Default)]
181 pub struct VirtualizationService {
182     state: Arc<Mutex<State>>,
183 }
184 
185 impl Interface for VirtualizationService {
dump(&self, writer: &mut dyn Write, _args: &[&CStr]) -> Result<(), StatusCode>186     fn dump(&self, writer: &mut dyn Write, _args: &[&CStr]) -> Result<(), StatusCode> {
187         check_permission("android.permission.DUMP").or(Err(StatusCode::PERMISSION_DENIED))?;
188         let state = &mut *self.state.lock().unwrap();
189         let vms = state.vms();
190         writeln!(writer, "Running {0} VMs:", vms.len()).or(Err(StatusCode::UNKNOWN_ERROR))?;
191         for vm in vms {
192             writeln!(writer, "VM CID: {}", vm.cid).or(Err(StatusCode::UNKNOWN_ERROR))?;
193             writeln!(writer, "\tState: {:?}", vm.vm_state.lock().unwrap())
194                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
195             writeln!(writer, "\tPayload state {:?}", vm.payload_state())
196                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
197             writeln!(writer, "\tProtected: {}", vm.protected).or(Err(StatusCode::UNKNOWN_ERROR))?;
198             writeln!(writer, "\ttemporary_directory: {}", vm.temporary_directory.to_string_lossy())
199                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
200             writeln!(writer, "\trequester_uid: {}", vm.requester_uid)
201                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
202             writeln!(writer, "\trequester_debug_pid: {}", vm.requester_debug_pid)
203                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
204         }
205         Ok(())
206     }
207 }
208 impl IVirtualizationService for VirtualizationService {
209     /// Creates (but does not start) a new VM with the given configuration, assigning it the next
210     /// available CID.
211     ///
212     /// 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>, ) -> binder::Result<Strong<dyn IVirtualMachine>>213     fn createVm(
214         &self,
215         config: &VirtualMachineConfig,
216         console_out_fd: Option<&ParcelFileDescriptor>,
217         console_in_fd: Option<&ParcelFileDescriptor>,
218         log_fd: Option<&ParcelFileDescriptor>,
219     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
220         let mut is_protected = false;
221         let ret = self.create_vm_internal(
222             config,
223             console_out_fd,
224             console_in_fd,
225             log_fd,
226             &mut is_protected,
227         );
228         write_vm_creation_stats(config, is_protected, &ret);
229         ret
230     }
231 
232     /// Allocate a new instance_id to the VM
allocateInstanceId(&self) -> binder::Result<[u8; 64]>233     fn allocateInstanceId(&self) -> binder::Result<[u8; 64]> {
234         check_manage_access()?;
235         GLOBAL_SERVICE.allocateInstanceId()
236     }
237 
238     /// 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<()>239     fn initializeWritablePartition(
240         &self,
241         image_fd: &ParcelFileDescriptor,
242         size_bytes: i64,
243         partition_type: PartitionType,
244     ) -> binder::Result<()> {
245         check_manage_access()?;
246         let size_bytes = size_bytes
247             .try_into()
248             .with_context(|| format!("Invalid size: {}", size_bytes))
249             .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?;
250         let size_bytes = round_up(size_bytes, PARTITION_GRANULARITY_BYTES);
251         let mut image = clone_file(image_fd)?;
252         // initialize the file. Any data in the file will be erased.
253         image
254             .seek(SeekFrom::Start(0))
255             .context("failed to move cursor to start")
256             .or_service_specific_exception(-1)?;
257         image.set_len(0).context("Failed to reset a file").or_service_specific_exception(-1)?;
258         // Set the file length. In most filesystems, this will not allocate any physical disk
259         // space, it will only change the logical size.
260         image
261             .set_len(size_bytes)
262             .context("Failed to extend file")
263             .or_service_specific_exception(-1)?;
264 
265         match partition_type {
266             PartitionType::RAW => Ok(()),
267             PartitionType::ANDROID_VM_INSTANCE => format_as_android_vm_instance(&mut image),
268             PartitionType::ENCRYPTEDSTORE => format_as_encryptedstore(&mut image),
269             _ => Err(Error::new(
270                 ErrorKind::Unsupported,
271                 format!("Unsupported partition type {:?}", partition_type),
272             )),
273         }
274         .with_context(|| format!("Failed to initialize partition as {:?}", partition_type))
275         .or_service_specific_exception(-1)?;
276 
277         Ok(())
278     }
279 
280     /// Creates or update the idsig file by digesting the input APK file.
createOrUpdateIdsigFile( &self, input_fd: &ParcelFileDescriptor, idsig_fd: &ParcelFileDescriptor, ) -> binder::Result<()>281     fn createOrUpdateIdsigFile(
282         &self,
283         input_fd: &ParcelFileDescriptor,
284         idsig_fd: &ParcelFileDescriptor,
285     ) -> binder::Result<()> {
286         check_manage_access()?;
287 
288         create_or_update_idsig_file(input_fd, idsig_fd).or_service_specific_exception(-1)?;
289         Ok(())
290     }
291 
292     /// Get a list of all currently running VMs. This method is only intended for debug purposes,
293     /// and as such is only permitted from the shell user.
debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>>294     fn debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>> {
295         // Delegate to the global service, including checking the debug permission.
296         GLOBAL_SERVICE.debugListVms()
297     }
298 
299     /// Get a list of assignable device types.
getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>>300     fn getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>> {
301         // Delegate to the global service, including checking the permission.
302         GLOBAL_SERVICE.getAssignableDevices()
303     }
304 
305     /// Get a list of supported OSes.
getSupportedOSList(&self) -> binder::Result<Vec<String>>306     fn getSupportedOSList(&self) -> binder::Result<Vec<String>> {
307         Ok(Vec::from_iter(SUPPORTED_OS_NAMES.iter().cloned()))
308     }
309 
310     /// Returns whether given feature is enabled
isFeatureEnabled(&self, feature: &str) -> binder::Result<bool>311     fn isFeatureEnabled(&self, feature: &str) -> binder::Result<bool> {
312         check_manage_access()?;
313         Ok(avf_features::is_feature_enabled(feature))
314     }
315 
enableTestAttestation(&self) -> binder::Result<()>316     fn enableTestAttestation(&self) -> binder::Result<()> {
317         GLOBAL_SERVICE.enableTestAttestation()
318     }
319 
isRemoteAttestationSupported(&self) -> binder::Result<bool>320     fn isRemoteAttestationSupported(&self) -> binder::Result<bool> {
321         check_manage_access()?;
322         GLOBAL_SERVICE.isRemoteAttestationSupported()
323     }
324 
isUpdatableVmSupported(&self) -> binder::Result<bool>325     fn isUpdatableVmSupported(&self) -> binder::Result<bool> {
326         // The response is specific to Microdroid. Updatable VMs are only possible if device
327         // supports Secretkeeper. Guest OS needs to use Secretkeeper based secrets. Microdroid does
328         // this, however other guest OSes may do things differently.
329         check_manage_access()?;
330         Ok(is_secretkeeper_supported())
331     }
332 
removeVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()>333     fn removeVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()> {
334         check_manage_access()?;
335         GLOBAL_SERVICE.removeVmInstance(instance_id)
336     }
337 
claimVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()>338     fn claimVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()> {
339         check_manage_access()?;
340         GLOBAL_SERVICE.claimVmInstance(instance_id)
341     }
342 }
343 
344 impl VirtualizationService {
init() -> VirtualizationService345     pub fn init() -> VirtualizationService {
346         VirtualizationService::default()
347     }
348 
create_vm_context( &self, requester_debug_pid: pid_t, ) -> binder::Result<(VmContext, Cid, PathBuf)>349     fn create_vm_context(
350         &self,
351         requester_debug_pid: pid_t,
352     ) -> binder::Result<(VmContext, Cid, PathBuf)> {
353         const NUM_ATTEMPTS: usize = 5;
354 
355         for _ in 0..NUM_ATTEMPTS {
356             let vm_context = GLOBAL_SERVICE.allocateGlobalVmContext(requester_debug_pid)?;
357             let cid = vm_context.getCid()? as Cid;
358             let temp_dir: PathBuf = vm_context.getTemporaryDirectory()?.into();
359             let service = VirtualMachineService::new_binder(self.state.clone(), cid).as_binder();
360 
361             // Start VM service listening for connections from the new CID on port=CID.
362             let port = cid;
363             match RpcServer::new_vsock(service, cid, port) {
364                 Ok(vm_server) => {
365                     vm_server.start();
366                     return Ok((VmContext::new(vm_context, vm_server), cid, temp_dir));
367                 }
368                 Err(err) => {
369                     warn!("Could not start RpcServer on port {}: {}", port, err);
370                 }
371             }
372         }
373         Err(anyhow!("Too many attempts to create VM context failed"))
374             .or_service_specific_exception(-1)
375     }
376 
create_vm_internal( &self, config: &VirtualMachineConfig, console_out_fd: Option<&ParcelFileDescriptor>, console_in_fd: Option<&ParcelFileDescriptor>, log_fd: Option<&ParcelFileDescriptor>, is_protected: &mut bool, ) -> binder::Result<Strong<dyn IVirtualMachine>>377     fn create_vm_internal(
378         &self,
379         config: &VirtualMachineConfig,
380         console_out_fd: Option<&ParcelFileDescriptor>,
381         console_in_fd: Option<&ParcelFileDescriptor>,
382         log_fd: Option<&ParcelFileDescriptor>,
383         is_protected: &mut bool,
384     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
385         let requester_uid = get_calling_uid();
386         let requester_debug_pid = get_calling_pid();
387 
388         check_config_features(config)?;
389 
390         // Allocating VM context checks the MANAGE_VIRTUAL_MACHINE permission.
391         let (vm_context, cid, temporary_directory) = self.create_vm_context(requester_debug_pid)?;
392 
393         if is_custom_config(config) {
394             check_use_custom_virtual_machine()?;
395         }
396 
397         let gdb_port = extract_gdb_port(config);
398 
399         // Additional permission checks if caller request gdb.
400         if gdb_port.is_some() {
401             check_gdb_allowed(config)?;
402         }
403 
404         let device_tree_overlay = maybe_create_device_tree_overlay(config, &temporary_directory)?;
405 
406         let debug_config = DebugConfig::new(config);
407         let ramdump = if !uses_gki_kernel(config) && debug_config.is_ramdump_needed() {
408             Some(prepare_ramdump_file(&temporary_directory)?)
409         } else {
410             None
411         };
412 
413         let state = &mut *self.state.lock().unwrap();
414         let console_out_fd =
415             clone_or_prepare_logger_fd(&debug_config, console_out_fd, format!("Console({})", cid))?;
416         let console_in_fd = console_in_fd.map(clone_file).transpose()?;
417         let log_fd = clone_or_prepare_logger_fd(&debug_config, log_fd, format!("Log({})", cid))?;
418 
419         // Counter to generate unique IDs for temporary image files.
420         let mut next_temporary_image_id = 0;
421         // Files which are referred to from composite images. These must be mapped to the crosvm
422         // child process, and not closed before it is started.
423         let mut indirect_files = vec![];
424 
425         let (is_app_config, config) = match config {
426             VirtualMachineConfig::RawConfig(config) => (false, BorrowedOrOwned::Borrowed(config)),
427             VirtualMachineConfig::AppConfig(config) => {
428                 let config = load_app_config(config, &debug_config, &temporary_directory)
429                     .or_service_specific_exception_with(-1, |e| {
430                         *is_protected = config.protectedVm;
431                         let message = format!("Failed to load app config: {:?}", e);
432                         error!("{}", message);
433                         message
434                     })?;
435                 (true, BorrowedOrOwned::Owned(config))
436             }
437         };
438         let config = config.as_ref();
439         *is_protected = config.protectedVm;
440 
441         // Check if partition images are labeled incorrectly. This is to prevent random images
442         // which are not protected by the Android Verified Boot (e.g. bits downloaded by apps) from
443         // being loaded in a pVM. This applies to everything but the instance image in the raw
444         // config, and everything but the non-executable, generated partitions in the app
445         // config.
446         config
447             .disks
448             .iter()
449             .flat_map(|disk| disk.partitions.iter())
450             .filter(|partition| {
451                 if is_app_config {
452                     !is_safe_app_partition(&partition.label)
453                 } else {
454                     !is_safe_raw_partition(&partition.label)
455                 }
456             })
457             .try_for_each(check_label_for_partition)
458             .or_service_specific_exception(-1)?;
459 
460         // Check if files for payloads and bases are NOT coming from /vendor and /odm, as they may
461         // have unstable interfaces.
462         // TODO(b/316431494): remove once Treble interfaces are stabilized.
463         check_partitions_for_files(config).or_service_specific_exception(-1)?;
464 
465         let kernel = maybe_clone_file(&config.kernel)?;
466         let initrd = maybe_clone_file(&config.initrd)?;
467 
468         // In a protected VM, we require custom kernels to come from a trusted source (b/237054515).
469         if config.protectedVm {
470             check_label_for_kernel_files(&kernel, &initrd).or_service_specific_exception(-1)?;
471         }
472 
473         let zero_filler_path = temporary_directory.join("zero.img");
474         write_zero_filler(&zero_filler_path)
475             .context("Failed to make composite image")
476             .with_log()
477             .or_service_specific_exception(-1)?;
478 
479         // Assemble disk images if needed.
480         let disks = config
481             .disks
482             .iter()
483             .map(|disk| {
484                 assemble_disk_image(
485                     disk,
486                     &zero_filler_path,
487                     &temporary_directory,
488                     &mut next_temporary_image_id,
489                     &mut indirect_files,
490                 )
491             })
492             .collect::<Result<Vec<DiskFile>, _>>()?;
493 
494         let (cpus, host_cpu_topology) = match config.cpuTopology {
495             CpuTopology::MATCH_HOST => (None, true),
496             CpuTopology::ONE_CPU => (NonZeroU32::new(1), false),
497             val => {
498                 return Err(anyhow!("Failed to parse CPU topology value {:?}", val))
499                     .with_log()
500                     .or_service_specific_exception(-1);
501             }
502         };
503 
504         let (vfio_devices, dtbo) = if !config.devices.is_empty() {
505             let mut set = HashSet::new();
506             for device in config.devices.iter() {
507                 let path = canonicalize(device)
508                     .with_context(|| format!("can't canonicalize {device}"))
509                     .or_service_specific_exception(-1)?;
510                 if !set.insert(path) {
511                     return Err(anyhow!("duplicated device {device}"))
512                         .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
513                 }
514             }
515             let devices = GLOBAL_SERVICE.bindDevicesToVfioDriver(&config.devices)?;
516             let dtbo_file = File::from(
517                 GLOBAL_SERVICE
518                     .getDtboFile()?
519                     .as_ref()
520                     .try_clone()
521                     .context("Failed to create VM DTBO from ParcelFileDescriptor")
522                     .or_binder_exception(ExceptionCode::BAD_PARCELABLE)?,
523             );
524             (devices, Some(dtbo_file))
525         } else {
526             (vec![], None)
527         };
528         let display_config = if cfg!(paravirtualized_devices) {
529             config
530                 .displayConfig
531                 .as_ref()
532                 .map(DisplayConfig::new)
533                 .transpose()
534                 .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?
535         } else {
536             None
537         };
538         let gpu_config = if cfg!(paravirtualized_devices) {
539             config
540                 .gpuConfig
541                 .as_ref()
542                 .map(GpuConfig::new)
543                 .transpose()
544                 .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?
545         } else {
546             None
547         };
548 
549         let input_device_options = if cfg!(paravirtualized_devices) {
550             config
551                 .inputDevices
552                 .iter()
553                 .map(to_input_device_option_from)
554                 .collect::<Result<Vec<InputDeviceOption>, _>>()
555                 .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?
556         } else {
557             vec![]
558         };
559 
560         // Create TAP network interface if the VM supports network.
561         let tap = if cfg!(network) && config.networkSupported {
562             if *is_protected {
563                 return Err(anyhow!("Network feature is not supported for pVM yet"))
564                     .with_log()
565                     .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION)?;
566             }
567             Some(File::from(
568                 GLOBAL_SERVICE
569                     .createTapInterface(&get_this_pid().to_string())?
570                     .as_ref()
571                     .try_clone()
572                     .context("Failed to get TAP interface from ParcelFileDescriptor")
573                     .or_binder_exception(ExceptionCode::BAD_PARCELABLE)?,
574             ))
575         } else {
576             None
577         };
578         let virtio_snd_backend =
579             if cfg!(paravirtualized_devices) { Some(String::from("aaudio")) } else { None };
580 
581         // Actually start the VM.
582         let crosvm_config = CrosvmConfig {
583             cid,
584             name: config.name.clone(),
585             bootloader: maybe_clone_file(&config.bootloader)?,
586             kernel,
587             initrd,
588             disks,
589             params: config.params.to_owned(),
590             protected: *is_protected,
591             debug_config,
592             memory_mib: config.memoryMib.try_into().ok().and_then(NonZeroU32::new),
593             cpus,
594             host_cpu_topology,
595             console_out_fd,
596             console_in_fd,
597             log_fd,
598             ramdump,
599             indirect_files,
600             platform_version: parse_platform_version_req(&config.platformVersion)?,
601             detect_hangup: is_app_config,
602             gdb_port,
603             vfio_devices,
604             dtbo,
605             device_tree_overlay,
606             display_config,
607             input_device_options,
608             hugepages: config.hugePages,
609             tap,
610             virtio_snd_backend,
611             console_input_device: config.consoleInputDevice.clone(),
612             boost_uclamp: config.boostUclamp,
613             gpu_config,
614         };
615         let instance = Arc::new(
616             VmInstance::new(
617                 crosvm_config,
618                 temporary_directory,
619                 requester_uid,
620                 requester_debug_pid,
621                 vm_context,
622             )
623             .with_context(|| format!("Failed to create VM with config {:?}", config))
624             .with_log()
625             .or_service_specific_exception(-1)?,
626         );
627         state.add_vm(Arc::downgrade(&instance));
628         Ok(VirtualMachine::create(instance))
629     }
630 }
631 
632 /// Returns whether a VM config represents a "custom" virtual machine, which requires the
633 /// USE_CUSTOM_VIRTUAL_MACHINE.
is_custom_config(config: &VirtualMachineConfig) -> bool634 fn is_custom_config(config: &VirtualMachineConfig) -> bool {
635     match config {
636         // Any raw (non-Microdroid) VM is considered custom.
637         VirtualMachineConfig::RawConfig(_) => true,
638         VirtualMachineConfig::AppConfig(config) => {
639             // Some features are reserved for platform apps only, even when using
640             // VirtualMachineAppConfig. Almost all of these features are grouped in the
641             // CustomConfig struct:
642             // - controlling CPUs;
643             // - gdbPort is set, meaning that crosvm will start a gdb server;
644             // - using anything other than the default kernel;
645             // - specifying devices to be assigned.
646             if config.customConfig.is_some() {
647                 true
648             } else {
649                 // Additional custom features not included in CustomConfig:
650                 // - specifying a config file;
651                 // - specifying extra APKs;
652                 // - specifying an OS other than Microdroid.
653                 (match &config.payload {
654                     Payload::ConfigPath(_) => true,
655                     Payload::PayloadConfig(payload_config) => !payload_config.extraApks.is_empty(),
656                 }) || config.osName != MICRODROID_OS_NAME
657             }
658         }
659     }
660 }
661 
extract_vendor_hashtree_digest(config: &VirtualMachineConfig) -> Result<Option<Vec<u8>>>662 fn extract_vendor_hashtree_digest(config: &VirtualMachineConfig) -> Result<Option<Vec<u8>>> {
663     let VirtualMachineConfig::AppConfig(config) = config else {
664         return Ok(None);
665     };
666     let Some(custom_config) = &config.customConfig else {
667         return Ok(None);
668     };
669     let Some(file) = custom_config.vendorImage.as_ref() else {
670         return Ok(None);
671     };
672 
673     let file = clone_file(file)?;
674     let size =
675         file.metadata().context("Failed to get metadata from microdroid vendor image")?.len();
676     let vbmeta = VbMetaImage::verify_reader_region(&file, 0, size)
677         .context("Failed to get vbmeta from microdroid-vendor.img")?;
678 
679     for descriptor in vbmeta.descriptors()?.iter() {
680         if let vbmeta::Descriptor::Hashtree(_) = descriptor {
681             let root_digest = hex::encode(descriptor.to_hashtree()?.root_digest());
682             return Ok(Some(root_digest.as_bytes().to_vec()));
683         }
684     }
685     Err(anyhow!("No hashtree digest is extracted from microdroid vendor image"))
686 }
687 
maybe_create_device_tree_overlay( config: &VirtualMachineConfig, temporary_directory: &Path, ) -> binder::Result<Option<File>>688 fn maybe_create_device_tree_overlay(
689     config: &VirtualMachineConfig,
690     temporary_directory: &Path,
691 ) -> binder::Result<Option<File>> {
692     // Currently, VirtMgr adds the host copy of reference DT & untrusted properties
693     // (e.g. instance-id)
694     let host_ref_dt = Path::new(VM_REFERENCE_DT_ON_HOST_PATH);
695     let host_ref_dt = if host_ref_dt.exists()
696         && read_dir(host_ref_dt).or_service_specific_exception(-1)?.next().is_some()
697     {
698         Some(host_ref_dt)
699     } else {
700         warn!("VM reference DT doesn't exist in host DT");
701         None
702     };
703 
704     let vendor_hashtree_digest = extract_vendor_hashtree_digest(config)
705         .context("Failed to extract vendor hashtree digest")
706         .or_service_specific_exception(-1)?;
707 
708     let trusted_props = if let Some(ref vendor_hashtree_digest) = vendor_hashtree_digest {
709         info!(
710             "Passing vendor hashtree digest to pvmfw. This will be rejected if it doesn't \
711                 match the trusted digest in the pvmfw config, causing the VM to fail to start."
712         );
713         vec![(cstr!("vendor_hashtree_descriptor_root_digest"), vendor_hashtree_digest.as_slice())]
714     } else {
715         vec![]
716     };
717 
718     let instance_id;
719     let mut untrusted_props = Vec::with_capacity(2);
720     if cfg!(llpvm_changes) {
721         instance_id = extract_instance_id(config);
722         untrusted_props.push((cstr!("instance-id"), &instance_id[..]));
723         let want_updatable = extract_want_updatable(config);
724         if want_updatable && is_secretkeeper_supported() {
725             // Let guest know that it can defer rollback protection to Secretkeeper by setting
726             // an empty property in untrusted node in DT. This enables Updatable VMs.
727             untrusted_props.push((cstr!("defer-rollback-protection"), &[]))
728         }
729     }
730 
731     let device_tree_overlay = if host_ref_dt.is_some()
732         || !untrusted_props.is_empty()
733         || !trusted_props.is_empty()
734     {
735         let dt_output = temporary_directory.join(VM_DT_OVERLAY_PATH);
736         let mut data = [0_u8; VM_DT_OVERLAY_MAX_SIZE];
737         let fdt =
738             create_device_tree_overlay(&mut data, host_ref_dt, &untrusted_props, &trusted_props)
739                 .map_err(|e| anyhow!("Failed to create DT overlay, {e:?}"))
740                 .or_service_specific_exception(-1)?;
741         fs::write(&dt_output, fdt.as_slice()).or_service_specific_exception(-1)?;
742         Some(File::open(dt_output).or_service_specific_exception(-1)?)
743     } else {
744         None
745     };
746     Ok(device_tree_overlay)
747 }
748 
write_zero_filler(zero_filler_path: &Path) -> Result<()>749 fn write_zero_filler(zero_filler_path: &Path) -> Result<()> {
750     let file = OpenOptions::new()
751         .create_new(true)
752         .read(true)
753         .write(true)
754         .open(zero_filler_path)
755         .with_context(|| "Failed to create zero.img")?;
756     file.set_len(ZERO_FILLER_SIZE)?;
757     Ok(())
758 }
759 
format_as_android_vm_instance(part: &mut dyn Write) -> std::io::Result<()>760 fn format_as_android_vm_instance(part: &mut dyn Write) -> std::io::Result<()> {
761     part.write_all(ANDROID_VM_INSTANCE_MAGIC.as_bytes())?;
762     part.write_all(&ANDROID_VM_INSTANCE_VERSION.to_le_bytes())?;
763     part.flush()
764 }
765 
format_as_encryptedstore(part: &mut dyn Write) -> std::io::Result<()>766 fn format_as_encryptedstore(part: &mut dyn Write) -> std::io::Result<()> {
767     part.write_all(UNFORMATTED_STORAGE_MAGIC.as_bytes())?;
768     part.flush()
769 }
770 
round_up(input: u64, granularity: u64) -> u64771 fn round_up(input: u64, granularity: u64) -> u64 {
772     if granularity == 0 {
773         return input;
774     }
775     // If the input is absurdly large we round down instead of up; it's going to fail anyway.
776     let result = input.checked_add(granularity - 1).unwrap_or(input);
777     (result / granularity) * granularity
778 }
779 
to_input_device_option_from(input_device: &InputDevice) -> Result<InputDeviceOption>780 fn to_input_device_option_from(input_device: &InputDevice) -> Result<InputDeviceOption> {
781     Ok(match input_device {
782         InputDevice::SingleTouch(single_touch) => InputDeviceOption::SingleTouch {
783             file: clone_file(single_touch.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?)?,
784             height: u32::try_from(single_touch.height)?,
785             width: u32::try_from(single_touch.width)?,
786             name: if !single_touch.name.is_empty() {
787                 Some(single_touch.name.clone())
788             } else {
789                 None
790             },
791         },
792         InputDevice::EvDev(evdev) => InputDeviceOption::EvDev(clone_file(
793             evdev.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?,
794         )?),
795         InputDevice::Keyboard(keyboard) => InputDeviceOption::Keyboard(clone_file(
796             keyboard.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?,
797         )?),
798         InputDevice::Mouse(mouse) => InputDeviceOption::Mouse(clone_file(
799             mouse.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?,
800         )?),
801     })
802 }
803 /// Given the configuration for a disk image, assembles the `DiskFile` to pass to crosvm.
804 ///
805 /// 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>806 fn assemble_disk_image(
807     disk: &DiskImage,
808     zero_filler_path: &Path,
809     temporary_directory: &Path,
810     next_temporary_image_id: &mut u64,
811     indirect_files: &mut Vec<File>,
812 ) -> Result<DiskFile, Status> {
813     let image = if !disk.partitions.is_empty() {
814         if disk.image.is_some() {
815             warn!("DiskImage {:?} contains both image and partitions.", disk);
816             return Err(anyhow!("DiskImage contains both image and partitions"))
817                 .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
818         }
819 
820         let composite_image_filenames =
821             make_composite_image_filenames(temporary_directory, next_temporary_image_id);
822         let (image, partition_files) = make_composite_image(
823             &disk.partitions,
824             zero_filler_path,
825             &composite_image_filenames.composite,
826             &composite_image_filenames.header,
827             &composite_image_filenames.footer,
828         )
829         .with_context(|| format!("Failed to make composite disk image with config {:?}", disk))
830         .with_log()
831         .or_service_specific_exception(-1)?;
832 
833         // Pass the file descriptors for the various partition files to crosvm when it
834         // is run.
835         indirect_files.extend(partition_files);
836 
837         image
838     } else if let Some(image) = &disk.image {
839         clone_file(image)?
840     } else {
841         warn!("DiskImage {:?} didn't contain image or partitions.", disk);
842         return Err(anyhow!("DiskImage didn't contain image or partitions."))
843             .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
844     };
845 
846     Ok(DiskFile { image, writable: disk.writable })
847 }
848 
append_kernel_param(param: &str, vm_config: &mut VirtualMachineRawConfig)849 fn append_kernel_param(param: &str, vm_config: &mut VirtualMachineRawConfig) {
850     if let Some(ref mut params) = vm_config.params {
851         params.push(' ');
852         params.push_str(param)
853     } else {
854         vm_config.params = Some(param.to_owned())
855     }
856 }
857 
extract_os_name_from_config_path(config: &Path) -> Option<String>858 fn extract_os_name_from_config_path(config: &Path) -> Option<String> {
859     if config.extension()?.to_str()? != "json" {
860         return None;
861     }
862 
863     Some(config.with_extension("").file_name()?.to_str()?.to_owned())
864 }
865 
extract_os_names_from_configs(config_glob_pattern: &str) -> Result<HashSet<String>>866 fn extract_os_names_from_configs(config_glob_pattern: &str) -> Result<HashSet<String>> {
867     let configs = glob(config_glob_pattern)?.collect::<Result<Vec<_>, _>>()?;
868     let os_names =
869         configs.iter().filter_map(|x| extract_os_name_from_config_path(x)).collect::<HashSet<_>>();
870 
871     Ok(os_names)
872 }
873 
get_supported_os_names() -> Result<HashSet<String>>874 fn get_supported_os_names() -> Result<HashSet<String>> {
875     if !cfg!(vendor_modules) {
876         return Ok(iter::once(MICRODROID_OS_NAME.to_owned()).collect());
877     }
878 
879     extract_os_names_from_configs("/apex/com.android.virt/etc/microdroid*.json")
880 }
881 
is_valid_os(os_name: &str) -> bool882 fn is_valid_os(os_name: &str) -> bool {
883     SUPPORTED_OS_NAMES.contains(os_name)
884 }
885 
uses_gki_kernel(config: &VirtualMachineConfig) -> bool886 fn uses_gki_kernel(config: &VirtualMachineConfig) -> bool {
887     if !cfg!(vendor_modules) {
888         return false;
889     }
890     match config {
891         VirtualMachineConfig::RawConfig(_) => false,
892         VirtualMachineConfig::AppConfig(config) => config.osName.starts_with("microdroid_gki-"),
893     }
894 }
895 
load_app_config( config: &VirtualMachineAppConfig, debug_config: &DebugConfig, temporary_directory: &Path, ) -> Result<VirtualMachineRawConfig>896 fn load_app_config(
897     config: &VirtualMachineAppConfig,
898     debug_config: &DebugConfig,
899     temporary_directory: &Path,
900 ) -> Result<VirtualMachineRawConfig> {
901     let apk_file = clone_file(config.apk.as_ref().unwrap())?;
902     let idsig_file = clone_file(config.idsig.as_ref().unwrap())?;
903     let instance_file = clone_file(config.instanceImage.as_ref().unwrap())?;
904 
905     let storage_image = if let Some(file) = config.encryptedStorageImage.as_ref() {
906         Some(clone_file(file)?)
907     } else {
908         None
909     };
910 
911     let vm_payload_config;
912     let extra_apk_files: Vec<_>;
913     match &config.payload {
914         Payload::ConfigPath(config_path) => {
915             vm_payload_config =
916                 load_vm_payload_config_from_file(&apk_file, config_path.as_str())
917                     .with_context(|| format!("Couldn't read config from {}", config_path))?;
918             extra_apk_files = vm_payload_config
919                 .extra_apks
920                 .iter()
921                 .enumerate()
922                 .map(|(i, apk)| {
923                     File::open(PathBuf::from(&apk.path))
924                         .with_context(|| format!("Failed to open extra apk #{i} {}", apk.path))
925                 })
926                 .collect::<Result<_>>()?;
927         }
928         Payload::PayloadConfig(payload_config) => {
929             vm_payload_config = create_vm_payload_config(payload_config)?;
930             extra_apk_files =
931                 payload_config.extraApks.iter().map(clone_file).collect::<binder::Result<_>>()?;
932         }
933     };
934 
935     let payload_config_os = vm_payload_config.os.name.as_str();
936     if !payload_config_os.is_empty() && payload_config_os != "microdroid" {
937         bail!("'os' in payload config is deprecated");
938     }
939 
940     // For now, the only supported OS is Microdroid and Microdroid GKI
941     let os_name = config.osName.as_str();
942     if !is_valid_os(os_name) {
943         bail!("Unknown OS \"{}\"", os_name);
944     }
945 
946     // It is safe to construct a filename based on the os_name because we've already checked that it
947     // is one of the allowed values.
948     let vm_config_path = PathBuf::from(format!("/apex/com.android.virt/etc/{}.json", os_name));
949     let vm_config_file = File::open(vm_config_path)?;
950     let mut vm_config = VmConfig::load(&vm_config_file)?.to_parcelable()?;
951 
952     if let Some(custom_config) = &config.customConfig {
953         if let Some(file) = custom_config.customKernelImage.as_ref() {
954             vm_config.kernel = Some(ParcelFileDescriptor::new(clone_file(file)?))
955         }
956         vm_config.gdbPort = custom_config.gdbPort;
957 
958         if let Some(file) = custom_config.vendorImage.as_ref() {
959             add_microdroid_vendor_image(clone_file(file)?, &mut vm_config);
960             append_kernel_param("androidboot.microdroid.mount_vendor=1", &mut vm_config)
961         }
962 
963         vm_config.devices.clone_from(&custom_config.devices);
964         vm_config.networkSupported = custom_config.networkSupported;
965     }
966 
967     if config.memoryMib > 0 {
968         vm_config.memoryMib = config.memoryMib;
969     }
970 
971     vm_config.name.clone_from(&config.name);
972     vm_config.protectedVm = config.protectedVm;
973     vm_config.cpuTopology = config.cpuTopology;
974     vm_config.hugePages = config.hugePages || vm_payload_config.hugepages;
975     vm_config.boostUclamp = config.boostUclamp;
976 
977     // Microdroid takes additional init ramdisk & (optionally) storage image
978     add_microdroid_system_images(config, instance_file, storage_image, os_name, &mut vm_config)?;
979 
980     // Include Microdroid payload disk (contains apks, idsigs) in vm config
981     add_microdroid_payload_images(
982         config,
983         debug_config,
984         temporary_directory,
985         apk_file,
986         idsig_file,
987         extra_apk_files,
988         &vm_payload_config,
989         &mut vm_config,
990     )?;
991 
992     Ok(vm_config)
993 }
994 
check_partition_for_file(fd: &ParcelFileDescriptor) -> Result<()>995 fn check_partition_for_file(fd: &ParcelFileDescriptor) -> Result<()> {
996     let path = format!("/proc/self/fd/{}", fd.as_raw_fd());
997     let link = fs::read_link(&path).context(format!("can't read_link {path}"))?;
998 
999     // microdroid vendor image is OK
1000     if cfg!(vendor_modules) && link == Path::new("/vendor/etc/avf/microdroid/microdroid_vendor.img")
1001     {
1002         return Ok(());
1003     }
1004 
1005     if link.starts_with("/vendor") || link.starts_with("/odm") {
1006         bail!("vendor or odm file {} can't be used for VM", link.display());
1007     }
1008 
1009     Ok(())
1010 }
1011 
check_partitions_for_files(config: &VirtualMachineRawConfig) -> Result<()>1012 fn check_partitions_for_files(config: &VirtualMachineRawConfig) -> Result<()> {
1013     config
1014         .disks
1015         .iter()
1016         .flat_map(|disk| disk.partitions.iter())
1017         .filter_map(|partition| partition.image.as_ref())
1018         .try_for_each(check_partition_for_file)?;
1019 
1020     config.kernel.as_ref().map_or(Ok(()), check_partition_for_file)?;
1021     config.initrd.as_ref().map_or(Ok(()), check_partition_for_file)?;
1022     config.bootloader.as_ref().map_or(Ok(()), check_partition_for_file)?;
1023 
1024     Ok(())
1025 }
1026 
load_vm_payload_config_from_file(apk_file: &File, config_path: &str) -> Result<VmPayloadConfig>1027 fn load_vm_payload_config_from_file(apk_file: &File, config_path: &str) -> Result<VmPayloadConfig> {
1028     let mut apk_zip = ZipArchive::new(apk_file)?;
1029     let config_file = apk_zip.by_name(config_path)?;
1030     Ok(serde_json::from_reader(config_file)?)
1031 }
1032 
create_vm_payload_config( payload_config: &VirtualMachinePayloadConfig, ) -> Result<VmPayloadConfig>1033 fn create_vm_payload_config(
1034     payload_config: &VirtualMachinePayloadConfig,
1035 ) -> Result<VmPayloadConfig> {
1036     // There isn't an actual config file. Construct a synthetic VmPayloadConfig from the explicit
1037     // parameters we've been given. Microdroid will do something equivalent inside the VM using the
1038     // payload config that we send it via the metadata file.
1039 
1040     let payload_binary_name = &payload_config.payloadBinaryName;
1041     if payload_binary_name.contains('/') {
1042         bail!("Payload binary name must not specify a path: {payload_binary_name}");
1043     }
1044 
1045     let task = Task { type_: TaskType::MicrodroidLauncher, command: payload_binary_name.clone() };
1046 
1047     // The VM only cares about how many there are, these names are actually ignored.
1048     let extra_apk_count = payload_config.extraApks.len();
1049     let extra_apks =
1050         (0..extra_apk_count).map(|i| ApkConfig { path: format!("extra-apk-{i}") }).collect();
1051 
1052     Ok(VmPayloadConfig { task: Some(task), extra_apks, ..Default::default() })
1053 }
1054 
1055 /// Generates a unique filename to use for a composite disk image.
make_composite_image_filenames( temporary_directory: &Path, next_temporary_image_id: &mut u64, ) -> CompositeImageFilenames1056 fn make_composite_image_filenames(
1057     temporary_directory: &Path,
1058     next_temporary_image_id: &mut u64,
1059 ) -> CompositeImageFilenames {
1060     let id = *next_temporary_image_id;
1061     *next_temporary_image_id += 1;
1062     CompositeImageFilenames {
1063         composite: temporary_directory.join(format!("composite-{}.img", id)),
1064         header: temporary_directory.join(format!("composite-{}-header.img", id)),
1065         footer: temporary_directory.join(format!("composite-{}-footer.img", id)),
1066     }
1067 }
1068 
1069 /// Filenames for a composite disk image, including header and footer partitions.
1070 #[derive(Clone, Debug, Eq, PartialEq)]
1071 struct CompositeImageFilenames {
1072     /// The composite disk image itself.
1073     composite: PathBuf,
1074     /// The header partition image.
1075     header: PathBuf,
1076     /// The footer partition image.
1077     footer: PathBuf,
1078 }
1079 
1080 /// Checks whether the caller has a specific permission
check_permission(perm: &str) -> binder::Result<()>1081 fn check_permission(perm: &str) -> binder::Result<()> {
1082     let calling_pid = get_calling_pid();
1083     let calling_uid = get_calling_uid();
1084     // Root can do anything
1085     if calling_uid == 0 {
1086         return Ok(());
1087     }
1088     let perm_svc: Strong<dyn IPermissionController::IPermissionController> =
1089         binder::wait_for_interface("permission")?;
1090     if perm_svc.checkPermission(perm, calling_pid, calling_uid as i32)? {
1091         Ok(())
1092     } else {
1093         Err(anyhow!("does not have the {} permission", perm))
1094             .or_binder_exception(ExceptionCode::SECURITY)
1095     }
1096 }
1097 
1098 /// Check whether the caller of the current Binder method is allowed to manage VMs
check_manage_access() -> binder::Result<()>1099 fn check_manage_access() -> binder::Result<()> {
1100     check_permission("android.permission.MANAGE_VIRTUAL_MACHINE")
1101 }
1102 
1103 /// Check whether the caller of the current Binder method is allowed to create custom VMs
check_use_custom_virtual_machine() -> binder::Result<()>1104 fn check_use_custom_virtual_machine() -> binder::Result<()> {
1105     check_permission("android.permission.USE_CUSTOM_VIRTUAL_MACHINE")
1106 }
1107 
1108 /// Return whether a partition is exempt from selinux label checks, because we know that it does
1109 /// not contain code and is likely to be generated in an app-writable directory.
is_safe_app_partition(label: &str) -> bool1110 fn is_safe_app_partition(label: &str) -> bool {
1111     // See add_microdroid_system_images & add_microdroid_payload_images in payload.rs.
1112     label == "vm-instance"
1113         || label == "encryptedstore"
1114         || label == "microdroid-apk-idsig"
1115         || label == "payload-metadata"
1116         || label.starts_with("extra-idsig-")
1117 }
1118 
1119 /// Returns whether a partition with the given label is safe for a raw config VM.
is_safe_raw_partition(label: &str) -> bool1120 fn is_safe_raw_partition(label: &str) -> bool {
1121     label == "vm-instance"
1122 }
1123 
1124 /// Check that a file SELinux label is acceptable.
1125 ///
1126 /// We only want to allow code in a VM to be sourced from places that apps, and the
1127 /// system or vendor, do not have write access to.
1128 ///
1129 /// Note that sepolicy must also grant read access for these types to both virtualization
1130 /// service and crosvm.
1131 ///
1132 /// App private data files are deliberately excluded, to avoid arbitrary payloads being run on
1133 /// user devices (W^X).
check_label_is_allowed(context: &SeContext) -> Result<()>1134 fn check_label_is_allowed(context: &SeContext) -> Result<()> {
1135     match context.selinux_type()? {
1136         | "apk_data_file" // APKs of an installed app
1137         | "shell_data_file" // test files created via adb shell
1138         | "staging_data_file" // updated/staged APEX images
1139         | "system_file" // immutable dm-verity protected partition
1140         | "virtualizationservice_data_file" // files created by VS / VirtMgr
1141         | "vendor_microdroid_file" // immutable dm-verity protected partition (/vendor/etc/avf/microdroid/.*)
1142          => Ok(()),
1143         _ => bail!("Label {} is not allowed", context),
1144     }
1145 }
1146 
check_label_for_partition(partition: &Partition) -> Result<()>1147 fn check_label_for_partition(partition: &Partition) -> Result<()> {
1148     let file = partition.image.as_ref().unwrap().as_ref();
1149     check_label_is_allowed(&getfilecon(file)?)
1150         .with_context(|| format!("Partition {} invalid", &partition.label))
1151 }
1152 
check_label_for_kernel_files(kernel: &Option<File>, initrd: &Option<File>) -> Result<()>1153 fn check_label_for_kernel_files(kernel: &Option<File>, initrd: &Option<File>) -> Result<()> {
1154     if let Some(f) = kernel {
1155         check_label_for_file(f, "kernel")?;
1156     }
1157     if let Some(f) = initrd {
1158         check_label_for_file(f, "initrd")?;
1159     }
1160     Ok(())
1161 }
check_label_for_file(file: &File, name: &str) -> Result<()>1162 fn check_label_for_file(file: &File, name: &str) -> Result<()> {
1163     check_label_is_allowed(&getfilecon(file)?).with_context(|| format!("{} file invalid", name))
1164 }
1165 
1166 /// Implementation of the AIDL `IVirtualMachine` interface. Used as a handle to a VM.
1167 #[derive(Debug)]
1168 struct VirtualMachine {
1169     instance: Arc<VmInstance>,
1170 }
1171 
1172 impl VirtualMachine {
create(instance: Arc<VmInstance>) -> Strong<dyn IVirtualMachine>1173     fn create(instance: Arc<VmInstance>) -> Strong<dyn IVirtualMachine> {
1174         BnVirtualMachine::new_binder(VirtualMachine { instance }, BinderFeatures::default())
1175     }
1176 }
1177 
1178 impl Interface for VirtualMachine {}
1179 
1180 impl IVirtualMachine for VirtualMachine {
getCid(&self) -> binder::Result<i32>1181     fn getCid(&self) -> binder::Result<i32> {
1182         // Don't check permission. The owner of the VM might have passed this binder object to
1183         // others.
1184         Ok(self.instance.cid as i32)
1185     }
1186 
getState(&self) -> binder::Result<VirtualMachineState>1187     fn getState(&self) -> binder::Result<VirtualMachineState> {
1188         // Don't check permission. The owner of the VM might have passed this binder object to
1189         // others.
1190         Ok(get_state(&self.instance))
1191     }
1192 
registerCallback( &self, callback: &Strong<dyn IVirtualMachineCallback>, ) -> binder::Result<()>1193     fn registerCallback(
1194         &self,
1195         callback: &Strong<dyn IVirtualMachineCallback>,
1196     ) -> binder::Result<()> {
1197         // Don't check permission. The owner of the VM might have passed this binder object to
1198         // others.
1199         //
1200         // TODO: Should this give an error if the VM is already dead?
1201         self.instance.callbacks.add(callback.clone());
1202         Ok(())
1203     }
1204 
start(&self) -> binder::Result<()>1205     fn start(&self) -> binder::Result<()> {
1206         self.instance
1207             .start()
1208             .with_context(|| format!("Error starting VM with CID {}", self.instance.cid))
1209             .with_log()
1210             .or_service_specific_exception(-1)
1211     }
1212 
stop(&self) -> binder::Result<()>1213     fn stop(&self) -> binder::Result<()> {
1214         self.instance
1215             .kill()
1216             .with_context(|| format!("Error stopping VM with CID {}", self.instance.cid))
1217             .with_log()
1218             .or_service_specific_exception(-1)
1219     }
1220 
onTrimMemory(&self, level: MemoryTrimLevel) -> binder::Result<()>1221     fn onTrimMemory(&self, level: MemoryTrimLevel) -> binder::Result<()> {
1222         self.instance
1223             .trim_memory(level)
1224             .with_context(|| format!("Error trimming VM with CID {}", self.instance.cid))
1225             .with_log()
1226             .or_service_specific_exception(-1)
1227     }
1228 
connectVsock(&self, port: i32) -> binder::Result<ParcelFileDescriptor>1229     fn connectVsock(&self, port: i32) -> binder::Result<ParcelFileDescriptor> {
1230         if !matches!(&*self.instance.vm_state.lock().unwrap(), VmState::Running { .. }) {
1231             return Err(anyhow!("VM is not running")).or_service_specific_exception(-1);
1232         }
1233         let port = port as u32;
1234         if port < 1024 {
1235             return Err(anyhow!("Can't connect to privileged port {port}"))
1236                 .or_service_specific_exception(-1);
1237         }
1238         let stream = VsockStream::connect_with_cid_port(self.instance.cid, port)
1239             .context("Failed to connect")
1240             .or_service_specific_exception(-1)?;
1241         Ok(vsock_stream_to_pfd(stream))
1242     }
1243 }
1244 
1245 impl Drop for VirtualMachine {
drop(&mut self)1246     fn drop(&mut self) {
1247         debug!("Dropping {:?}", self);
1248         if let Err(e) = self.instance.kill() {
1249             debug!("Error stopping dropped VM with CID {}: {:?}", self.instance.cid, e);
1250         }
1251     }
1252 }
1253 
1254 /// A set of Binders to be called back in response to various events on the VM, such as when it
1255 /// dies.
1256 #[derive(Debug, Default)]
1257 pub struct VirtualMachineCallbacks(Mutex<Vec<Strong<dyn IVirtualMachineCallback>>>);
1258 
1259 impl VirtualMachineCallbacks {
1260     /// Call all registered callbacks to notify that the payload has started.
notify_payload_started(&self, cid: Cid)1261     pub fn notify_payload_started(&self, cid: Cid) {
1262         let callbacks = &*self.0.lock().unwrap();
1263         for callback in callbacks {
1264             if let Err(e) = callback.onPayloadStarted(cid as i32) {
1265                 error!("Error notifying payload start event from VM CID {}: {:?}", cid, e);
1266             }
1267         }
1268     }
1269 
1270     /// Call all registered callbacks to notify that the payload is ready to serve.
notify_payload_ready(&self, cid: Cid)1271     pub fn notify_payload_ready(&self, cid: Cid) {
1272         let callbacks = &*self.0.lock().unwrap();
1273         for callback in callbacks {
1274             if let Err(e) = callback.onPayloadReady(cid as i32) {
1275                 error!("Error notifying payload ready event from VM CID {}: {:?}", cid, e);
1276             }
1277         }
1278     }
1279 
1280     /// Call all registered callbacks to notify that the payload has finished.
notify_payload_finished(&self, cid: Cid, exit_code: i32)1281     pub fn notify_payload_finished(&self, cid: Cid, exit_code: i32) {
1282         let callbacks = &*self.0.lock().unwrap();
1283         for callback in callbacks {
1284             if let Err(e) = callback.onPayloadFinished(cid as i32, exit_code) {
1285                 error!("Error notifying payload finish event from VM CID {}: {:?}", cid, e);
1286             }
1287         }
1288     }
1289 
1290     /// Call all registered callbacks to say that the VM encountered an error.
notify_error(&self, cid: Cid, error_code: ErrorCode, message: &str)1291     pub fn notify_error(&self, cid: Cid, error_code: ErrorCode, message: &str) {
1292         let callbacks = &*self.0.lock().unwrap();
1293         for callback in callbacks {
1294             if let Err(e) = callback.onError(cid as i32, error_code, message) {
1295                 error!("Error notifying error event from VM CID {}: {:?}", cid, e);
1296             }
1297         }
1298     }
1299 
1300     /// Call all registered callbacks to say that the VM has died.
callback_on_died(&self, cid: Cid, reason: DeathReason)1301     pub fn callback_on_died(&self, cid: Cid, reason: DeathReason) {
1302         let callbacks = &*self.0.lock().unwrap();
1303         for callback in callbacks {
1304             if let Err(e) = callback.onDied(cid as i32, reason) {
1305                 error!("Error notifying exit of VM CID {}: {:?}", cid, e);
1306             }
1307         }
1308     }
1309 
1310     /// Add a new callback to the set.
add(&self, callback: Strong<dyn IVirtualMachineCallback>)1311     fn add(&self, callback: Strong<dyn IVirtualMachineCallback>) {
1312         self.0.lock().unwrap().push(callback);
1313     }
1314 }
1315 
1316 /// The mutable state of the VirtualizationService. There should only be one instance of this
1317 /// struct.
1318 #[derive(Debug, Default)]
1319 struct State {
1320     /// The VMs which have been started. When VMs are started a weak reference is added to this
1321     /// list while a strong reference is returned to the caller over Binder. Once all copies of
1322     /// the Binder client are dropped the weak reference here will become invalid, and will be
1323     /// removed from the list opportunistically the next time `add_vm` is called.
1324     vms: Vec<Weak<VmInstance>>,
1325 }
1326 
1327 impl State {
1328     /// Get a list of VMs which still have Binder references to them.
vms(&self) -> Vec<Arc<VmInstance>>1329     fn vms(&self) -> Vec<Arc<VmInstance>> {
1330         // Attempt to upgrade the weak pointers to strong pointers.
1331         self.vms.iter().filter_map(Weak::upgrade).collect()
1332     }
1333 
1334     /// Add a new VM to the list.
add_vm(&mut self, vm: Weak<VmInstance>)1335     fn add_vm(&mut self, vm: Weak<VmInstance>) {
1336         // Garbage collect any entries from the stored list which no longer exist.
1337         self.vms.retain(|vm| vm.strong_count() > 0);
1338 
1339         // Actually add the new VM.
1340         self.vms.push(vm);
1341     }
1342 
1343     /// Get a VM that corresponds to the given cid
get_vm(&self, cid: Cid) -> Option<Arc<VmInstance>>1344     fn get_vm(&self, cid: Cid) -> Option<Arc<VmInstance>> {
1345         self.vms().into_iter().find(|vm| vm.cid == cid)
1346     }
1347 }
1348 
1349 /// Gets the `VirtualMachineState` of the given `VmInstance`.
get_state(instance: &VmInstance) -> VirtualMachineState1350 fn get_state(instance: &VmInstance) -> VirtualMachineState {
1351     match &*instance.vm_state.lock().unwrap() {
1352         VmState::NotStarted { .. } => VirtualMachineState::NOT_STARTED,
1353         VmState::Running { .. } => match instance.payload_state() {
1354             PayloadState::Starting => VirtualMachineState::STARTING,
1355             PayloadState::Started => VirtualMachineState::STARTED,
1356             PayloadState::Ready => VirtualMachineState::READY,
1357             PayloadState::Finished => VirtualMachineState::FINISHED,
1358             PayloadState::Hangup => VirtualMachineState::DEAD,
1359         },
1360         VmState::Dead => VirtualMachineState::DEAD,
1361         VmState::Failed => VirtualMachineState::DEAD,
1362     }
1363 }
1364 
1365 /// Converts a `&ParcelFileDescriptor` to a `File` by cloning the file.
clone_file(file: &ParcelFileDescriptor) -> binder::Result<File>1366 pub fn clone_file(file: &ParcelFileDescriptor) -> binder::Result<File> {
1367     file.as_ref()
1368         .try_clone()
1369         .context("Failed to clone File from ParcelFileDescriptor")
1370         .or_binder_exception(ExceptionCode::BAD_PARCELABLE)
1371         .map(File::from)
1372 }
1373 
1374 /// Converts an `&Option<ParcelFileDescriptor>` to an `Option<File>` by cloning the file.
maybe_clone_file(file: &Option<ParcelFileDescriptor>) -> binder::Result<Option<File>>1375 fn maybe_clone_file(file: &Option<ParcelFileDescriptor>) -> binder::Result<Option<File>> {
1376     file.as_ref().map(clone_file).transpose()
1377 }
1378 
1379 /// Converts a `VsockStream` to a `ParcelFileDescriptor`.
vsock_stream_to_pfd(stream: VsockStream) -> ParcelFileDescriptor1380 fn vsock_stream_to_pfd(stream: VsockStream) -> ParcelFileDescriptor {
1381     // SAFETY: ownership is transferred from stream to f
1382     let f = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
1383     ParcelFileDescriptor::new(f)
1384 }
1385 
1386 /// Parses the platform version requirement string.
parse_platform_version_req(s: &str) -> binder::Result<VersionReq>1387 fn parse_platform_version_req(s: &str) -> binder::Result<VersionReq> {
1388     VersionReq::parse(s)
1389         .with_context(|| format!("Invalid platform version requirement {}", s))
1390         .or_binder_exception(ExceptionCode::BAD_PARCELABLE)
1391 }
1392 
1393 /// Create the empty ramdump file
prepare_ramdump_file(temporary_directory: &Path) -> binder::Result<File>1394 fn prepare_ramdump_file(temporary_directory: &Path) -> binder::Result<File> {
1395     // `ramdump_write` is sent to crosvm and will be the backing store for the /dev/hvc1 where
1396     // VM will emit ramdump to. `ramdump_read` will be sent back to the client (i.e. the VM
1397     // owner) for readout.
1398     let ramdump_path = temporary_directory.join("ramdump");
1399     let ramdump = File::create(ramdump_path)
1400         .context("Failed to prepare ramdump file")
1401         .with_log()
1402         .or_service_specific_exception(-1)?;
1403     Ok(ramdump)
1404 }
1405 
is_protected(config: &VirtualMachineConfig) -> bool1406 fn is_protected(config: &VirtualMachineConfig) -> bool {
1407     match config {
1408         VirtualMachineConfig::RawConfig(config) => config.protectedVm,
1409         VirtualMachineConfig::AppConfig(config) => config.protectedVm,
1410     }
1411 }
1412 
check_gdb_allowed(config: &VirtualMachineConfig) -> binder::Result<()>1413 fn check_gdb_allowed(config: &VirtualMachineConfig) -> binder::Result<()> {
1414     if is_protected(config) {
1415         return Err(anyhow!("Can't use gdb with protected VMs"))
1416             .or_binder_exception(ExceptionCode::SECURITY);
1417     }
1418 
1419     if get_debug_level(config) == Some(DebugLevel::NONE) {
1420         return Err(anyhow!("Can't use gdb with non-debuggable VMs"))
1421             .or_binder_exception(ExceptionCode::SECURITY);
1422     }
1423 
1424     Ok(())
1425 }
1426 
extract_instance_id(config: &VirtualMachineConfig) -> [u8; 64]1427 fn extract_instance_id(config: &VirtualMachineConfig) -> [u8; 64] {
1428     match config {
1429         VirtualMachineConfig::RawConfig(config) => config.instanceId,
1430         VirtualMachineConfig::AppConfig(config) => config.instanceId,
1431     }
1432 }
1433 
extract_want_updatable(config: &VirtualMachineConfig) -> bool1434 fn extract_want_updatable(config: &VirtualMachineConfig) -> bool {
1435     match config {
1436         VirtualMachineConfig::RawConfig(_) => true,
1437         VirtualMachineConfig::AppConfig(config) => {
1438             let Some(custom) = &config.customConfig else { return true };
1439             custom.wantUpdatable
1440         }
1441     }
1442 }
1443 
extract_gdb_port(config: &VirtualMachineConfig) -> Option<NonZeroU16>1444 fn extract_gdb_port(config: &VirtualMachineConfig) -> Option<NonZeroU16> {
1445     match config {
1446         VirtualMachineConfig::RawConfig(config) => NonZeroU16::new(config.gdbPort as u16),
1447         VirtualMachineConfig::AppConfig(config) => {
1448             NonZeroU16::new(config.customConfig.as_ref().map(|c| c.gdbPort).unwrap_or(0) as u16)
1449         }
1450     }
1451 }
1452 
check_no_vendor_modules(config: &VirtualMachineConfig) -> binder::Result<()>1453 fn check_no_vendor_modules(config: &VirtualMachineConfig) -> binder::Result<()> {
1454     let VirtualMachineConfig::AppConfig(config) = config else { return Ok(()) };
1455     if let Some(custom_config) = &config.customConfig {
1456         if custom_config.vendorImage.is_some() || custom_config.customKernelImage.is_some() {
1457             return Err(anyhow!("vendor modules feature is disabled"))
1458                 .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
1459         }
1460     }
1461     Ok(())
1462 }
1463 
check_no_devices(config: &VirtualMachineConfig) -> binder::Result<()>1464 fn check_no_devices(config: &VirtualMachineConfig) -> binder::Result<()> {
1465     let VirtualMachineConfig::AppConfig(config) = config else { return Ok(()) };
1466     if let Some(custom_config) = &config.customConfig {
1467         if !custom_config.devices.is_empty() {
1468             return Err(anyhow!("device assignment feature is disabled"))
1469                 .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
1470         }
1471     }
1472     Ok(())
1473 }
1474 
check_no_extra_apks(config: &VirtualMachineConfig) -> binder::Result<()>1475 fn check_no_extra_apks(config: &VirtualMachineConfig) -> binder::Result<()> {
1476     let VirtualMachineConfig::AppConfig(config) = config else { return Ok(()) };
1477     let Payload::PayloadConfig(payload_config) = &config.payload else { return Ok(()) };
1478     if !payload_config.extraApks.is_empty() {
1479         return Err(anyhow!("multi-tenant feature is disabled"))
1480             .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
1481     }
1482     Ok(())
1483 }
1484 
check_config_features(config: &VirtualMachineConfig) -> binder::Result<()>1485 fn check_config_features(config: &VirtualMachineConfig) -> binder::Result<()> {
1486     if !cfg!(vendor_modules) {
1487         check_no_vendor_modules(config)?;
1488     }
1489     if !cfg!(device_assignment) {
1490         check_no_devices(config)?;
1491     }
1492     if !cfg!(multi_tenant) {
1493         check_no_extra_apks(config)?;
1494     }
1495     Ok(())
1496 }
1497 
clone_or_prepare_logger_fd( debug_config: &DebugConfig, fd: Option<&ParcelFileDescriptor>, tag: String, ) -> Result<Option<File>, Status>1498 fn clone_or_prepare_logger_fd(
1499     debug_config: &DebugConfig,
1500     fd: Option<&ParcelFileDescriptor>,
1501     tag: String,
1502 ) -> Result<Option<File>, Status> {
1503     if let Some(fd) = fd {
1504         return Ok(Some(clone_file(fd)?));
1505     }
1506 
1507     if !debug_config.should_prepare_console_output() {
1508         return Ok(None);
1509     };
1510 
1511     let (read_fd, write_fd) =
1512         pipe().context("Failed to create pipe").or_service_specific_exception(-1)?;
1513 
1514     let mut reader = BufReader::new(File::from(read_fd));
1515     let write_fd = File::from(write_fd);
1516 
1517     std::thread::spawn(move || loop {
1518         let mut buf = vec![];
1519         match reader.read_until(b'\n', &mut buf) {
1520             Ok(0) => {
1521                 // EOF
1522                 return;
1523             }
1524             Ok(size) => {
1525                 if buf[size - 1] == b'\n' {
1526                     buf.pop();
1527                 }
1528                 info!("{}: {}", &tag, &String::from_utf8_lossy(&buf));
1529             }
1530             Err(e) => {
1531                 error!("Could not read console pipe: {:?}", e);
1532                 return;
1533             }
1534         };
1535     });
1536 
1537     Ok(Some(write_fd))
1538 }
1539 
1540 /// Simple utility for referencing Borrowed or Owned. Similar to std::borrow::Cow, but
1541 /// it doesn't require that T implements Clone.
1542 enum BorrowedOrOwned<'a, T> {
1543     Borrowed(&'a T),
1544     Owned(T),
1545 }
1546 
1547 impl<'a, T> AsRef<T> for BorrowedOrOwned<'a, T> {
as_ref(&self) -> &T1548     fn as_ref(&self) -> &T {
1549         match self {
1550             Self::Borrowed(b) => b,
1551             Self::Owned(o) => o,
1552         }
1553     }
1554 }
1555 
1556 /// Implementation of `IVirtualMachineService`, the entry point of the AIDL service.
1557 #[derive(Debug, Default)]
1558 struct VirtualMachineService {
1559     state: Arc<Mutex<State>>,
1560     cid: Cid,
1561 }
1562 
1563 impl Interface for VirtualMachineService {}
1564 
1565 impl IVirtualMachineService for VirtualMachineService {
notifyPayloadStarted(&self) -> binder::Result<()>1566     fn notifyPayloadStarted(&self) -> binder::Result<()> {
1567         let cid = self.cid;
1568         if let Some(vm) = self.state.lock().unwrap().get_vm(cid) {
1569             info!("VM with CID {} started payload", cid);
1570             vm.update_payload_state(PayloadState::Started)
1571                 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)?;
1572             vm.callbacks.notify_payload_started(cid);
1573 
1574             let vm_start_timestamp = vm.vm_metric.lock().unwrap().start_timestamp;
1575             write_vm_booted_stats(vm.requester_uid as i32, &vm.name, vm_start_timestamp);
1576             Ok(())
1577         } else {
1578             error!("notifyPayloadStarted is called from an unknown CID {}", cid);
1579             Err(anyhow!("cannot find a VM with CID {}", cid)).or_service_specific_exception(-1)
1580         }
1581     }
1582 
notifyPayloadReady(&self) -> binder::Result<()>1583     fn notifyPayloadReady(&self) -> binder::Result<()> {
1584         let cid = self.cid;
1585         if let Some(vm) = self.state.lock().unwrap().get_vm(cid) {
1586             info!("VM with CID {} reported payload is ready", cid);
1587             vm.update_payload_state(PayloadState::Ready)
1588                 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)?;
1589             vm.callbacks.notify_payload_ready(cid);
1590             Ok(())
1591         } else {
1592             error!("notifyPayloadReady is called from an unknown CID {}", cid);
1593             Err(anyhow!("cannot find a VM with CID {}", cid)).or_service_specific_exception(-1)
1594         }
1595     }
1596 
notifyPayloadFinished(&self, exit_code: i32) -> binder::Result<()>1597     fn notifyPayloadFinished(&self, exit_code: i32) -> binder::Result<()> {
1598         let cid = self.cid;
1599         if let Some(vm) = self.state.lock().unwrap().get_vm(cid) {
1600             info!("VM with CID {} finished payload", cid);
1601             vm.update_payload_state(PayloadState::Finished)
1602                 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)?;
1603             vm.callbacks.notify_payload_finished(cid, exit_code);
1604             Ok(())
1605         } else {
1606             error!("notifyPayloadFinished is called from an unknown CID {}", cid);
1607             Err(anyhow!("cannot find a VM with CID {}", cid)).or_service_specific_exception(-1)
1608         }
1609     }
1610 
notifyError(&self, error_code: ErrorCode, message: &str) -> binder::Result<()>1611     fn notifyError(&self, error_code: ErrorCode, message: &str) -> binder::Result<()> {
1612         let cid = self.cid;
1613         if let Some(vm) = self.state.lock().unwrap().get_vm(cid) {
1614             info!("VM with CID {} encountered an error", cid);
1615             vm.update_payload_state(PayloadState::Finished)
1616                 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)?;
1617             vm.callbacks.notify_error(cid, error_code, message);
1618             Ok(())
1619         } else {
1620             error!("notifyError is called from an unknown CID {}", cid);
1621             Err(anyhow!("cannot find a VM with CID {}", cid)).or_service_specific_exception(-1)
1622         }
1623     }
1624 
getSecretkeeper(&self) -> binder::Result<Strong<dyn ISecretkeeper>>1625     fn getSecretkeeper(&self) -> binder::Result<Strong<dyn ISecretkeeper>> {
1626         if !is_secretkeeper_supported() {
1627             return Err(StatusCode::NAME_NOT_FOUND)?;
1628         }
1629         let sk = binder::wait_for_interface(SECRETKEEPER_IDENTIFIER)?;
1630         Ok(BnSecretkeeper::new_binder(SecretkeeperProxy(sk), BinderFeatures::default()))
1631     }
1632 
requestAttestation(&self, csr: &[u8], test_mode: bool) -> binder::Result<Vec<Certificate>>1633     fn requestAttestation(&self, csr: &[u8], test_mode: bool) -> binder::Result<Vec<Certificate>> {
1634         GLOBAL_SERVICE.requestAttestation(csr, get_calling_uid() as i32, test_mode)
1635     }
1636 }
1637 
is_secretkeeper_supported() -> bool1638 fn is_secretkeeper_supported() -> bool {
1639     binder::is_declared(SECRETKEEPER_IDENTIFIER)
1640         .expect("Could not check for declared Secretkeeper interface")
1641 }
1642 
1643 impl VirtualMachineService {
new_binder(state: Arc<Mutex<State>>, cid: Cid) -> Strong<dyn IVirtualMachineService>1644     fn new_binder(state: Arc<Mutex<State>>, cid: Cid) -> Strong<dyn IVirtualMachineService> {
1645         BnVirtualMachineService::new_binder(
1646             VirtualMachineService { state, cid },
1647             BinderFeatures::default(),
1648         )
1649     }
1650 }
1651 
1652 struct SecretkeeperProxy(Strong<dyn ISecretkeeper>);
1653 
1654 impl Interface for SecretkeeperProxy {}
1655 
1656 impl ISecretkeeper for SecretkeeperProxy {
processSecretManagementRequest(&self, req: &[u8]) -> binder::Result<Vec<u8>>1657     fn processSecretManagementRequest(&self, req: &[u8]) -> binder::Result<Vec<u8>> {
1658         // Pass the request to the channel, and read the response.
1659         self.0.processSecretManagementRequest(req)
1660     }
1661 
getAuthGraphKe(&self) -> binder::Result<Strong<dyn IAuthGraphKeyExchange>>1662     fn getAuthGraphKe(&self) -> binder::Result<Strong<dyn IAuthGraphKeyExchange>> {
1663         let ag = AuthGraphKeyExchangeProxy(self.0.getAuthGraphKe()?);
1664         Ok(BnAuthGraphKeyExchange::new_binder(ag, BinderFeatures::default()))
1665     }
1666 
deleteIds(&self, ids: &[SecretId]) -> binder::Result<()>1667     fn deleteIds(&self, ids: &[SecretId]) -> binder::Result<()> {
1668         self.0.deleteIds(ids)
1669     }
1670 
deleteAll(&self) -> binder::Result<()>1671     fn deleteAll(&self) -> binder::Result<()> {
1672         self.0.deleteAll()
1673     }
1674 }
1675 
1676 struct AuthGraphKeyExchangeProxy(Strong<dyn IAuthGraphKeyExchange>);
1677 
1678 impl Interface for AuthGraphKeyExchangeProxy {}
1679 
1680 impl IAuthGraphKeyExchange for AuthGraphKeyExchangeProxy {
create(&self) -> binder::Result<SessionInitiationInfo>1681     fn create(&self) -> binder::Result<SessionInitiationInfo> {
1682         self.0.create()
1683     }
1684 
init( &self, peer_pub_key: &PubKey, peer_id: &Identity, peer_nonce: &[u8], peer_version: i32, ) -> binder::Result<KeInitResult>1685     fn init(
1686         &self,
1687         peer_pub_key: &PubKey,
1688         peer_id: &Identity,
1689         peer_nonce: &[u8],
1690         peer_version: i32,
1691     ) -> binder::Result<KeInitResult> {
1692         self.0.init(peer_pub_key, peer_id, peer_nonce, peer_version)
1693     }
1694 
finish( &self, peer_pub_key: &PubKey, peer_id: &Identity, peer_signature: &SessionIdSignature, peer_nonce: &[u8], peer_version: i32, own_key: &Key, ) -> binder::Result<SessionInfo>1695     fn finish(
1696         &self,
1697         peer_pub_key: &PubKey,
1698         peer_id: &Identity,
1699         peer_signature: &SessionIdSignature,
1700         peer_nonce: &[u8],
1701         peer_version: i32,
1702         own_key: &Key,
1703     ) -> binder::Result<SessionInfo> {
1704         self.0.finish(peer_pub_key, peer_id, peer_signature, peer_nonce, peer_version, own_key)
1705     }
1706 
authenticationComplete( &self, peer_signature: &SessionIdSignature, shared_keys: &[AuthgraphArc; 2], ) -> binder::Result<[AuthgraphArc; 2]>1707     fn authenticationComplete(
1708         &self,
1709         peer_signature: &SessionIdSignature,
1710         shared_keys: &[AuthgraphArc; 2],
1711     ) -> binder::Result<[AuthgraphArc; 2]> {
1712         self.0.authenticationComplete(peer_signature, shared_keys)
1713     }
1714 }
1715 
1716 #[cfg(test)]
1717 mod tests {
1718     use super::*;
1719 
1720     #[test]
test_is_allowed_label_for_partition() -> Result<()>1721     fn test_is_allowed_label_for_partition() -> Result<()> {
1722         let expected_results = vec![
1723             ("u:object_r:system_file:s0", true),
1724             ("u:object_r:apk_data_file:s0", true),
1725             ("u:object_r:app_data_file:s0", false),
1726             ("u:object_r:app_data_file:s0:c512,c768", false),
1727             ("u:object_r:privapp_data_file:s0:c512,c768", false),
1728             ("invalid", false),
1729             ("user:role:apk_data_file:severity:categories", true),
1730             ("user:role:apk_data_file:severity:categories:extraneous", false),
1731         ];
1732 
1733         for (label, expected_valid) in expected_results {
1734             let context = SeContext::new(label)?;
1735             let result = check_label_is_allowed(&context);
1736             if expected_valid {
1737                 assert!(result.is_ok(), "Expected label {} to be allowed, got {:?}", label, result);
1738             } else if result.is_ok() {
1739                 bail!("Expected label {} to be disallowed", label);
1740             }
1741         }
1742         Ok(())
1743     }
1744 
1745     #[test]
test_create_or_update_idsig_file_empty_apk() -> Result<()>1746     fn test_create_or_update_idsig_file_empty_apk() -> Result<()> {
1747         let apk = tempfile::tempfile().unwrap();
1748         let idsig = tempfile::tempfile().unwrap();
1749 
1750         let ret = create_or_update_idsig_file(
1751             &ParcelFileDescriptor::new(apk),
1752             &ParcelFileDescriptor::new(idsig),
1753         );
1754         assert!(ret.is_err(), "should fail");
1755         Ok(())
1756     }
1757 
1758     #[test]
test_create_or_update_idsig_dir_instead_of_file_for_apk() -> Result<()>1759     fn test_create_or_update_idsig_dir_instead_of_file_for_apk() -> Result<()> {
1760         let tmp_dir = tempfile::TempDir::new().unwrap();
1761         let apk = File::open(tmp_dir.path()).unwrap();
1762         let idsig = tempfile::tempfile().unwrap();
1763 
1764         let ret = create_or_update_idsig_file(
1765             &ParcelFileDescriptor::new(apk),
1766             &ParcelFileDescriptor::new(idsig),
1767         );
1768         assert!(ret.is_err(), "should fail");
1769         Ok(())
1770     }
1771 
1772     /// Verifies that create_or_update_idsig_file won't oom if a fd that corresponds to a directory
1773     /// on ext4 filesystem is passed.
1774     /// On ext4 lseek on a directory fd will return (off_t)-1 (see:
1775     /// https://bugzilla.kernel.org/show_bug.cgi?id=200043), which will result in
1776     /// create_or_update_idsig_file ooming while attempting to allocate petabytes of memory.
1777     #[test]
test_create_or_update_idsig_does_not_crash_dir_on_ext4() -> Result<()>1778     fn test_create_or_update_idsig_does_not_crash_dir_on_ext4() -> Result<()> {
1779         // APEXes are backed by the ext4.
1780         let apk = File::open("/apex/com.android.virt/").unwrap();
1781         let idsig = tempfile::tempfile().unwrap();
1782 
1783         let ret = create_or_update_idsig_file(
1784             &ParcelFileDescriptor::new(apk),
1785             &ParcelFileDescriptor::new(idsig),
1786         );
1787         assert!(ret.is_err(), "should fail");
1788         Ok(())
1789     }
1790 
1791     #[test]
test_create_or_update_idsig_does_not_update_if_already_valid() -> Result<()>1792     fn test_create_or_update_idsig_does_not_update_if_already_valid() -> Result<()> {
1793         use std::io::Seek;
1794 
1795         // Pick any APK
1796         let mut apk = File::open("/system/priv-app/Shell/Shell.apk").unwrap();
1797         let mut idsig = tempfile::tempfile().unwrap();
1798 
1799         create_or_update_idsig_file(
1800             &ParcelFileDescriptor::new(apk.try_clone()?),
1801             &ParcelFileDescriptor::new(idsig.try_clone()?),
1802         )?;
1803         let modified_orig = idsig.metadata()?.modified()?;
1804         apk.rewind()?;
1805         idsig.rewind()?;
1806 
1807         // Call the function again
1808         create_or_update_idsig_file(
1809             &ParcelFileDescriptor::new(apk.try_clone()?),
1810             &ParcelFileDescriptor::new(idsig.try_clone()?),
1811         )?;
1812         let modified_new = idsig.metadata()?.modified()?;
1813         assert!(modified_orig == modified_new, "idsig file was updated unnecessarily");
1814         Ok(())
1815     }
1816 
1817     #[test]
test_create_or_update_idsig_on_non_empty_file() -> Result<()>1818     fn test_create_or_update_idsig_on_non_empty_file() -> Result<()> {
1819         use std::io::Read;
1820 
1821         // Pick any APK
1822         let mut apk = File::open("/system/priv-app/Shell/Shell.apk").unwrap();
1823         let idsig_empty = tempfile::tempfile().unwrap();
1824         let mut idsig_invalid = tempfile::tempfile().unwrap();
1825         idsig_invalid.write_all(b"Oops")?;
1826 
1827         // Create new idsig
1828         create_or_update_idsig_file(
1829             &ParcelFileDescriptor::new(apk.try_clone()?),
1830             &ParcelFileDescriptor::new(idsig_empty.try_clone()?),
1831         )?;
1832         apk.rewind()?;
1833 
1834         // Update idsig_invalid
1835         create_or_update_idsig_file(
1836             &ParcelFileDescriptor::new(apk.try_clone()?),
1837             &ParcelFileDescriptor::new(idsig_invalid.try_clone()?),
1838         )?;
1839 
1840         // Ensure the 2 idsig files have same size!
1841         assert!(
1842             idsig_empty.metadata()?.len() == idsig_invalid.metadata()?.len(),
1843             "idsig files differ in size"
1844         );
1845         // Ensure the 2 idsig files have same content!
1846         for (b1, b2) in idsig_empty.bytes().zip(idsig_invalid.bytes()) {
1847             assert!(b1.unwrap() == b2.unwrap(), "idsig files differ")
1848         }
1849         Ok(())
1850     }
1851     #[test]
test_append_kernel_param_first_param()1852     fn test_append_kernel_param_first_param() {
1853         let mut vm_config = VirtualMachineRawConfig { ..Default::default() };
1854         append_kernel_param("foo=1", &mut vm_config);
1855         assert_eq!(vm_config.params, Some("foo=1".to_owned()))
1856     }
1857 
1858     #[test]
test_append_kernel_param()1859     fn test_append_kernel_param() {
1860         let mut vm_config =
1861             VirtualMachineRawConfig { params: Some("foo=5".to_owned()), ..Default::default() };
1862         append_kernel_param("bar=42", &mut vm_config);
1863         assert_eq!(vm_config.params, Some("foo=5 bar=42".to_owned()))
1864     }
1865 
test_extract_os_name_from_config_path( path: &Path, expected_result: Option<&str>, ) -> Result<()>1866     fn test_extract_os_name_from_config_path(
1867         path: &Path,
1868         expected_result: Option<&str>,
1869     ) -> Result<()> {
1870         let result = extract_os_name_from_config_path(path);
1871         if result.as_deref() != expected_result {
1872             bail!("Expected {:?} but was {:?}", expected_result, &result)
1873         }
1874         Ok(())
1875     }
1876 
1877     #[test]
test_extract_os_name_from_microdroid_config() -> Result<()>1878     fn test_extract_os_name_from_microdroid_config() -> Result<()> {
1879         test_extract_os_name_from_config_path(
1880             Path::new("/apex/com.android.virt/etc/microdroid.json"),
1881             Some("microdroid"),
1882         )
1883     }
1884 
1885     #[test]
test_extract_os_name_from_microdroid_gki_config() -> Result<()>1886     fn test_extract_os_name_from_microdroid_gki_config() -> Result<()> {
1887         test_extract_os_name_from_config_path(
1888             Path::new("/apex/com.android.virt/etc/microdroid_gki-android14-6.1.json"),
1889             Some("microdroid_gki-android14-6.1"),
1890         )
1891     }
1892 
1893     #[test]
test_extract_os_name_from_invalid_path() -> Result<()>1894     fn test_extract_os_name_from_invalid_path() -> Result<()> {
1895         test_extract_os_name_from_config_path(
1896             Path::new("/apex/com.android.virt/etc/microdroid.img"),
1897             None,
1898         )
1899     }
1900 
1901     #[test]
test_extract_os_name_from_configs() -> Result<()>1902     fn test_extract_os_name_from_configs() -> Result<()> {
1903         let tmp_dir = tempfile::TempDir::new()?;
1904         let tmp_dir_path = tmp_dir.path().to_owned();
1905 
1906         let mut os_names: HashSet<String> = HashSet::new();
1907         os_names.insert("microdroid".to_owned());
1908         os_names.insert("microdroid_gki-android14-6.1".to_owned());
1909         os_names.insert("microdroid_gki-android15-6.1".to_owned());
1910 
1911         // config files
1912         for os_name in &os_names {
1913             std::fs::write(tmp_dir_path.join(os_name.to_owned() + ".json"), b"")?;
1914         }
1915 
1916         // fake files not related to configs
1917         std::fs::write(tmp_dir_path.join("microdroid_super.img"), b"")?;
1918         std::fs::write(tmp_dir_path.join("microdroid_foobar.apk"), b"")?;
1919 
1920         let glob_pattern = match tmp_dir_path.join("microdroid*.json").to_str() {
1921             Some(s) => s.to_owned(),
1922             None => bail!("tmp_dir_path {:?} is not UTF-8", tmp_dir_path),
1923         };
1924 
1925         let result = extract_os_names_from_configs(&glob_pattern)?;
1926         if result != os_names {
1927             bail!("Expected {:?} but was {:?}", os_names, result);
1928         }
1929         Ok(())
1930     }
1931 }
1932