• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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};
18 use crate::atom::{
19     write_vm_booted_stats, write_vm_creation_stats};
20 use crate::composite::make_composite_image;
21 use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmContext, VmInstance, VmState};
22 use crate::debug_config::DebugConfig;
23 use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images};
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     DeathReason::DeathReason,
28     ErrorCode::ErrorCode,
29 };
30 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
31     CpuTopology::CpuTopology,
32     DiskImage::DiskImage,
33     IVirtualMachine::{BnVirtualMachine, IVirtualMachine},
34     IVirtualMachineCallback::IVirtualMachineCallback,
35     IVirtualizationService::IVirtualizationService,
36     MemoryTrimLevel::MemoryTrimLevel,
37     Partition::Partition,
38     PartitionType::PartitionType,
39     VirtualMachineAppConfig::{DebugLevel::DebugLevel, Payload::Payload, VirtualMachineAppConfig},
40     VirtualMachineConfig::VirtualMachineConfig,
41     VirtualMachineDebugInfo::VirtualMachineDebugInfo,
42     VirtualMachinePayloadConfig::VirtualMachinePayloadConfig,
43     VirtualMachineRawConfig::VirtualMachineRawConfig,
44     VirtualMachineState::VirtualMachineState,
45 };
46 use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IVirtualizationServiceInternal::IVirtualizationServiceInternal;
47 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
48         BnVirtualMachineService, IVirtualMachineService,
49 };
50 use anyhow::{anyhow, bail, Context, Result};
51 use apkverify::{HashAlgorithm, V4Signature};
52 use binder::{
53     self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, ParcelFileDescriptor,
54     Status, StatusCode, Strong,
55 };
56 use disk::QcowFile;
57 use lazy_static::lazy_static;
58 use log::{debug, error, info, warn};
59 use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
60 use nix::unistd::pipe;
61 use rpcbinder::RpcServer;
62 use rustutils::system_properties;
63 use semver::VersionReq;
64 use std::convert::TryInto;
65 use std::ffi::CStr;
66 use std::fs::{read_dir, remove_file, File, OpenOptions};
67 use std::io::{BufRead, BufReader, Error, ErrorKind, Write};
68 use std::num::{NonZeroU16, NonZeroU32};
69 use std::os::unix::io::{FromRawFd, IntoRawFd};
70 use std::os::unix::raw::pid_t;
71 use std::path::{Path, PathBuf};
72 use std::sync::{Arc, Mutex, Weak};
73 use vmconfig::VmConfig;
74 use vsock::VsockStream;
75 use zip::ZipArchive;
76 
77 /// The unique ID of a VM used (together with a port number) for vsock communication.
78 pub type Cid = u32;
79 
80 pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
81 
82 /// The size of zero.img.
83 /// Gaps in composite disk images are filled with a shared zero.img.
84 const ZERO_FILLER_SIZE: u64 = 4096;
85 
86 /// Magic string for the instance image
87 const ANDROID_VM_INSTANCE_MAGIC: &str = "Android-VM-instance";
88 
89 /// Version of the instance image format
90 const ANDROID_VM_INSTANCE_VERSION: u16 = 1;
91 
92 const MICRODROID_OS_NAME: &str = "microdroid";
93 
94 const UNFORMATTED_STORAGE_MAGIC: &str = "UNFORMATTED-STORAGE";
95 
96 /// crosvm requires all partitions to be a multiple of 4KiB.
97 const PARTITION_GRANULARITY_BYTES: u64 = 4096;
98 
99 lazy_static! {
100     pub static ref GLOBAL_SERVICE: Strong<dyn IVirtualizationServiceInternal> =
101         wait_for_interface(BINDER_SERVICE_IDENTIFIER)
102             .expect("Could not connect to VirtualizationServiceInternal");
103 }
104 
create_or_update_idsig_file( input_fd: &ParcelFileDescriptor, idsig_fd: &ParcelFileDescriptor, ) -> Result<()>105 fn create_or_update_idsig_file(
106     input_fd: &ParcelFileDescriptor,
107     idsig_fd: &ParcelFileDescriptor,
108 ) -> Result<()> {
109     let mut input = clone_file(input_fd)?;
110     let metadata = input.metadata().context("failed to get input metadata")?;
111     if !metadata.is_file() {
112         bail!("input is not a regular file");
113     }
114     let mut sig =
115         V4Signature::create(&mut input, get_current_sdk()?, 4096, &[], HashAlgorithm::SHA256)
116             .context("failed to create idsig")?;
117 
118     let mut output = clone_file(idsig_fd)?;
119     output.set_len(0).context("failed to set_len on the idsig output")?;
120     sig.write_into(&mut output).context("failed to write idsig")?;
121     Ok(())
122 }
123 
get_current_sdk() -> Result<u32>124 fn get_current_sdk() -> Result<u32> {
125     let current_sdk = system_properties::read("ro.build.version.sdk")?;
126     let current_sdk = current_sdk.ok_or_else(|| anyhow!("SDK version missing"))?;
127     current_sdk.parse().context("Malformed SDK version")
128 }
129 
remove_temporary_files(path: &PathBuf) -> Result<()>130 pub fn remove_temporary_files(path: &PathBuf) -> Result<()> {
131     for dir_entry in read_dir(path)? {
132         remove_file(dir_entry?.path())?;
133     }
134     Ok(())
135 }
136 
137 /// Implementation of `IVirtualizationService`, the entry point of the AIDL service.
138 #[derive(Debug, Default)]
139 pub struct VirtualizationService {
140     state: Arc<Mutex<State>>,
141 }
142 
143 impl Interface for VirtualizationService {
dump(&self, mut file: &File, _args: &[&CStr]) -> Result<(), StatusCode>144     fn dump(&self, mut file: &File, _args: &[&CStr]) -> Result<(), StatusCode> {
145         check_permission("android.permission.DUMP").or(Err(StatusCode::PERMISSION_DENIED))?;
146         let state = &mut *self.state.lock().unwrap();
147         let vms = state.vms();
148         writeln!(file, "Running {0} VMs:", vms.len()).or(Err(StatusCode::UNKNOWN_ERROR))?;
149         for vm in vms {
150             writeln!(file, "VM CID: {}", vm.cid).or(Err(StatusCode::UNKNOWN_ERROR))?;
151             writeln!(file, "\tState: {:?}", vm.vm_state.lock().unwrap())
152                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
153             writeln!(file, "\tPayload state {:?}", vm.payload_state())
154                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
155             writeln!(file, "\tProtected: {}", vm.protected).or(Err(StatusCode::UNKNOWN_ERROR))?;
156             writeln!(file, "\ttemporary_directory: {}", vm.temporary_directory.to_string_lossy())
157                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
158             writeln!(file, "\trequester_uid: {}", vm.requester_uid)
159                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
160             writeln!(file, "\trequester_debug_pid: {}", vm.requester_debug_pid)
161                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
162         }
163         Ok(())
164     }
165 }
166 
167 impl IVirtualizationService for VirtualizationService {
168     /// Creates (but does not start) a new VM with the given configuration, assigning it the next
169     /// available CID.
170     ///
171     /// Returns a binder `IVirtualMachine` object referring to it, as a handle for the client.
createVm( &self, config: &VirtualMachineConfig, console_fd: Option<&ParcelFileDescriptor>, log_fd: Option<&ParcelFileDescriptor>, ) -> binder::Result<Strong<dyn IVirtualMachine>>172     fn createVm(
173         &self,
174         config: &VirtualMachineConfig,
175         console_fd: Option<&ParcelFileDescriptor>,
176         log_fd: Option<&ParcelFileDescriptor>,
177     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
178         let mut is_protected = false;
179         let ret = self.create_vm_internal(config, console_fd, log_fd, &mut is_protected);
180         write_vm_creation_stats(config, is_protected, &ret);
181         ret
182     }
183 
184     /// 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<()>185     fn initializeWritablePartition(
186         &self,
187         image_fd: &ParcelFileDescriptor,
188         size_bytes: i64,
189         partition_type: PartitionType,
190     ) -> binder::Result<()> {
191         check_manage_access()?;
192         let size_bytes = size_bytes.try_into().map_err(|e| {
193             Status::new_exception_str(
194                 ExceptionCode::ILLEGAL_ARGUMENT,
195                 Some(format!("Invalid size {}: {:?}", size_bytes, e)),
196             )
197         })?;
198         let size_bytes = round_up(size_bytes, PARTITION_GRANULARITY_BYTES);
199         let image = clone_file(image_fd)?;
200         // initialize the file. Any data in the file will be erased.
201         image.set_len(0).map_err(|e| {
202             Status::new_service_specific_error_str(
203                 -1,
204                 Some(format!("Failed to reset a file: {:?}", e)),
205             )
206         })?;
207         let mut part = QcowFile::new(image, size_bytes).map_err(|e| {
208             Status::new_service_specific_error_str(
209                 -1,
210                 Some(format!("Failed to create QCOW2 image: {:?}", e)),
211             )
212         })?;
213 
214         match partition_type {
215             PartitionType::RAW => Ok(()),
216             PartitionType::ANDROID_VM_INSTANCE => format_as_android_vm_instance(&mut part),
217             PartitionType::ENCRYPTEDSTORE => format_as_encryptedstore(&mut part),
218             _ => Err(Error::new(
219                 ErrorKind::Unsupported,
220                 format!("Unsupported partition type {:?}", partition_type),
221             )),
222         }
223         .map_err(|e| {
224             Status::new_service_specific_error_str(
225                 -1,
226                 Some(format!("Failed to initialize partition as {:?}: {:?}", partition_type, e)),
227             )
228         })?;
229 
230         Ok(())
231     }
232 
233     /// Creates or update the idsig file by digesting the input APK file.
createOrUpdateIdsigFile( &self, input_fd: &ParcelFileDescriptor, idsig_fd: &ParcelFileDescriptor, ) -> binder::Result<()>234     fn createOrUpdateIdsigFile(
235         &self,
236         input_fd: &ParcelFileDescriptor,
237         idsig_fd: &ParcelFileDescriptor,
238     ) -> binder::Result<()> {
239         // TODO(b/193504400): do this only when (1) idsig_fd is empty or (2) the APK digest in
240         // idsig_fd is different from APK digest in input_fd
241 
242         check_manage_access()?;
243 
244         create_or_update_idsig_file(input_fd, idsig_fd)
245             .map_err(|e| Status::new_service_specific_error_str(-1, Some(format!("{:?}", e))))?;
246         Ok(())
247     }
248 
249     /// Get a list of all currently running VMs. This method is only intended for debug purposes,
250     /// and as such is only permitted from the shell user.
debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>>251     fn debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>> {
252         // Delegate to the global service, including checking the debug permission.
253         GLOBAL_SERVICE.debugListVms()
254     }
255 }
256 
257 impl VirtualizationService {
init() -> VirtualizationService258     pub fn init() -> VirtualizationService {
259         VirtualizationService::default()
260     }
261 
create_vm_context( &self, requester_debug_pid: pid_t, ) -> binder::Result<(VmContext, Cid, PathBuf)>262     fn create_vm_context(
263         &self,
264         requester_debug_pid: pid_t,
265     ) -> binder::Result<(VmContext, Cid, PathBuf)> {
266         const NUM_ATTEMPTS: usize = 5;
267 
268         for _ in 0..NUM_ATTEMPTS {
269             let vm_context = GLOBAL_SERVICE.allocateGlobalVmContext(requester_debug_pid)?;
270             let cid = vm_context.getCid()? as Cid;
271             let temp_dir: PathBuf = vm_context.getTemporaryDirectory()?.into();
272             let service = VirtualMachineService::new_binder(self.state.clone(), cid).as_binder();
273 
274             // Start VM service listening for connections from the new CID on port=CID.
275             let port = cid;
276             match RpcServer::new_vsock(service, cid, port) {
277                 Ok(vm_server) => {
278                     vm_server.start();
279                     return Ok((VmContext::new(vm_context, vm_server), cid, temp_dir));
280                 }
281                 Err(err) => {
282                     warn!("Could not start RpcServer on port {}: {}", port, err);
283                 }
284             }
285         }
286         Err(Status::new_service_specific_error_str(
287             -1,
288             Some("Too many attempts to create VM context failed."),
289         ))
290     }
291 
create_vm_internal( &self, config: &VirtualMachineConfig, console_fd: Option<&ParcelFileDescriptor>, log_fd: Option<&ParcelFileDescriptor>, is_protected: &mut bool, ) -> binder::Result<Strong<dyn IVirtualMachine>>292     fn create_vm_internal(
293         &self,
294         config: &VirtualMachineConfig,
295         console_fd: Option<&ParcelFileDescriptor>,
296         log_fd: Option<&ParcelFileDescriptor>,
297         is_protected: &mut bool,
298     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
299         let requester_uid = get_calling_uid();
300         let requester_debug_pid = get_calling_pid();
301 
302         // Allocating VM context checks the MANAGE_VIRTUAL_MACHINE permission.
303         let (vm_context, cid, temporary_directory) = self.create_vm_context(requester_debug_pid)?;
304 
305         let is_custom = match config {
306             VirtualMachineConfig::RawConfig(_) => true,
307             VirtualMachineConfig::AppConfig(config) => {
308                 // Some features are reserved for platform apps only, even when using
309                 // VirtualMachineAppConfig:
310                 // - controlling CPUs;
311                 // - specifying a config file in the APK;
312                 // - gdbPort is set, meaning that crosvm will start a gdb server.
313                 !config.taskProfiles.is_empty()
314                     || matches!(config.payload, Payload::ConfigPath(_))
315                     || config.gdbPort > 0
316             }
317         };
318         if is_custom {
319             check_use_custom_virtual_machine()?;
320         }
321 
322         let gdb_port = extract_gdb_port(config);
323 
324         // Additional permission checks if caller request gdb.
325         if gdb_port.is_some() {
326             check_gdb_allowed(config)?;
327         }
328 
329         let debug_level = match config {
330             VirtualMachineConfig::AppConfig(config) => config.debugLevel,
331             _ => DebugLevel::NONE,
332         };
333         let debug_config = DebugConfig::new(debug_level);
334 
335         let ramdump = if debug_config.is_ramdump_needed() {
336             Some(prepare_ramdump_file(&temporary_directory)?)
337         } else {
338             None
339         };
340 
341         let state = &mut *self.state.lock().unwrap();
342         let console_fd =
343             clone_or_prepare_logger_fd(&debug_config, console_fd, format!("Console({})", cid))?;
344         let log_fd = clone_or_prepare_logger_fd(&debug_config, log_fd, format!("Log({})", cid))?;
345 
346         // Counter to generate unique IDs for temporary image files.
347         let mut next_temporary_image_id = 0;
348         // Files which are referred to from composite images. These must be mapped to the crosvm
349         // child process, and not closed before it is started.
350         let mut indirect_files = vec![];
351 
352         let (is_app_config, config) = match config {
353             VirtualMachineConfig::RawConfig(config) => (false, BorrowedOrOwned::Borrowed(config)),
354             VirtualMachineConfig::AppConfig(config) => {
355                 let config =
356                     load_app_config(config, &debug_config, &temporary_directory).map_err(|e| {
357                         *is_protected = config.protectedVm;
358                         let message = format!("Failed to load app config: {:?}", e);
359                         error!("{}", message);
360                         Status::new_service_specific_error_str(-1, Some(message))
361                     })?;
362                 (true, BorrowedOrOwned::Owned(config))
363             }
364         };
365         let config = config.as_ref();
366         *is_protected = config.protectedVm;
367 
368         // Check if partition images are labeled incorrectly. This is to prevent random images
369         // which are not protected by the Android Verified Boot (e.g. bits downloaded by apps) from
370         // being loaded in a pVM. This applies to everything in the raw config, and everything but
371         // the non-executable, generated partitions in the app config.
372         config
373             .disks
374             .iter()
375             .flat_map(|disk| disk.partitions.iter())
376             .filter(|partition| {
377                 if is_app_config {
378                     !is_safe_app_partition(&partition.label)
379                 } else {
380                     true // all partitions are checked
381                 }
382             })
383             .try_for_each(check_label_for_partition)
384             .map_err(|e| Status::new_service_specific_error_str(-1, Some(format!("{:?}", e))))?;
385 
386         let kernel = maybe_clone_file(&config.kernel)?;
387         let initrd = maybe_clone_file(&config.initrd)?;
388 
389         // In a protected VM, we require custom kernels to come from a trusted source (b/237054515).
390         if config.protectedVm {
391             check_label_for_kernel_files(&kernel, &initrd).map_err(|e| {
392                 Status::new_service_specific_error_str(-1, Some(format!("{:?}", e)))
393             })?;
394         }
395 
396         let zero_filler_path = temporary_directory.join("zero.img");
397         write_zero_filler(&zero_filler_path).map_err(|e| {
398             error!("Failed to make composite image: {:?}", e);
399             Status::new_service_specific_error_str(
400                 -1,
401                 Some(format!("Failed to make composite image: {:?}", e)),
402             )
403         })?;
404 
405         // Assemble disk images if needed.
406         let disks = config
407             .disks
408             .iter()
409             .map(|disk| {
410                 assemble_disk_image(
411                     disk,
412                     &zero_filler_path,
413                     &temporary_directory,
414                     &mut next_temporary_image_id,
415                     &mut indirect_files,
416                 )
417             })
418             .collect::<Result<Vec<DiskFile>, _>>()?;
419 
420         let (cpus, host_cpu_topology) = match config.cpuTopology {
421             CpuTopology::MATCH_HOST => (None, true),
422             CpuTopology::ONE_CPU => (NonZeroU32::new(1), false),
423             val => {
424                 error!("Unexpected value of CPU topology: {:?}", val);
425                 return Err(Status::new_service_specific_error_str(
426                     -1,
427                     Some(format!("Failed to parse CPU topology value: {:?}", val)),
428                 ));
429             }
430         };
431 
432         // Actually start the VM.
433         let crosvm_config = CrosvmConfig {
434             cid,
435             name: config.name.clone(),
436             bootloader: maybe_clone_file(&config.bootloader)?,
437             kernel,
438             initrd,
439             disks,
440             params: config.params.to_owned(),
441             protected: *is_protected,
442             debug_config,
443             memory_mib: config.memoryMib.try_into().ok().and_then(NonZeroU32::new),
444             cpus,
445             host_cpu_topology,
446             task_profiles: config.taskProfiles.clone(),
447             console_fd,
448             log_fd,
449             ramdump,
450             indirect_files,
451             platform_version: parse_platform_version_req(&config.platformVersion)?,
452             detect_hangup: is_app_config,
453             gdb_port,
454         };
455         let instance = Arc::new(
456             VmInstance::new(
457                 crosvm_config,
458                 temporary_directory,
459                 requester_uid,
460                 requester_debug_pid,
461                 vm_context,
462             )
463             .map_err(|e| {
464                 error!("Failed to create VM with config {:?}: {:?}", config, e);
465                 Status::new_service_specific_error_str(
466                     -1,
467                     Some(format!("Failed to create VM: {:?}", e)),
468                 )
469             })?,
470         );
471         state.add_vm(Arc::downgrade(&instance));
472         Ok(VirtualMachine::create(instance))
473     }
474 }
475 
write_zero_filler(zero_filler_path: &Path) -> Result<()>476 fn write_zero_filler(zero_filler_path: &Path) -> Result<()> {
477     let file = OpenOptions::new()
478         .create_new(true)
479         .read(true)
480         .write(true)
481         .open(zero_filler_path)
482         .with_context(|| "Failed to create zero.img")?;
483     file.set_len(ZERO_FILLER_SIZE)?;
484     Ok(())
485 }
486 
format_as_android_vm_instance(part: &mut dyn Write) -> std::io::Result<()>487 fn format_as_android_vm_instance(part: &mut dyn Write) -> std::io::Result<()> {
488     part.write_all(ANDROID_VM_INSTANCE_MAGIC.as_bytes())?;
489     part.write_all(&ANDROID_VM_INSTANCE_VERSION.to_le_bytes())?;
490     part.flush()
491 }
492 
format_as_encryptedstore(part: &mut dyn Write) -> std::io::Result<()>493 fn format_as_encryptedstore(part: &mut dyn Write) -> std::io::Result<()> {
494     part.write_all(UNFORMATTED_STORAGE_MAGIC.as_bytes())?;
495     part.flush()
496 }
497 
round_up(input: u64, granularity: u64) -> u64498 fn round_up(input: u64, granularity: u64) -> u64 {
499     if granularity == 0 {
500         return input;
501     }
502     // If the input is absurdly large we round down instead of up; it's going to fail anyway.
503     let result = input.checked_add(granularity - 1).unwrap_or(input);
504     (result / granularity) * granularity
505 }
506 
507 /// Given the configuration for a disk image, assembles the `DiskFile` to pass to crosvm.
508 ///
509 /// 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>510 fn assemble_disk_image(
511     disk: &DiskImage,
512     zero_filler_path: &Path,
513     temporary_directory: &Path,
514     next_temporary_image_id: &mut u64,
515     indirect_files: &mut Vec<File>,
516 ) -> Result<DiskFile, Status> {
517     let image = if !disk.partitions.is_empty() {
518         if disk.image.is_some() {
519             warn!("DiskImage {:?} contains both image and partitions.", disk);
520             return Err(Status::new_exception_str(
521                 ExceptionCode::ILLEGAL_ARGUMENT,
522                 Some("DiskImage contains both image and partitions."),
523             ));
524         }
525 
526         let composite_image_filenames =
527             make_composite_image_filenames(temporary_directory, next_temporary_image_id);
528         let (image, partition_files) = make_composite_image(
529             &disk.partitions,
530             zero_filler_path,
531             &composite_image_filenames.composite,
532             &composite_image_filenames.header,
533             &composite_image_filenames.footer,
534         )
535         .map_err(|e| {
536             error!("Failed to make composite image with config {:?}: {:?}", disk, e);
537             Status::new_service_specific_error_str(
538                 -1,
539                 Some(format!("Failed to make composite image: {:?}", e)),
540             )
541         })?;
542 
543         // Pass the file descriptors for the various partition files to crosvm when it
544         // is run.
545         indirect_files.extend(partition_files);
546 
547         image
548     } else if let Some(image) = &disk.image {
549         clone_file(image)?
550     } else {
551         warn!("DiskImage {:?} didn't contain image or partitions.", disk);
552         return Err(Status::new_exception_str(
553             ExceptionCode::ILLEGAL_ARGUMENT,
554             Some("DiskImage didn't contain image or partitions."),
555         ));
556     };
557 
558     Ok(DiskFile { image, writable: disk.writable })
559 }
560 
load_app_config( config: &VirtualMachineAppConfig, debug_config: &DebugConfig, temporary_directory: &Path, ) -> Result<VirtualMachineRawConfig>561 fn load_app_config(
562     config: &VirtualMachineAppConfig,
563     debug_config: &DebugConfig,
564     temporary_directory: &Path,
565 ) -> Result<VirtualMachineRawConfig> {
566     let apk_file = clone_file(config.apk.as_ref().unwrap())?;
567     let idsig_file = clone_file(config.idsig.as_ref().unwrap())?;
568     let instance_file = clone_file(config.instanceImage.as_ref().unwrap())?;
569 
570     let storage_image = if let Some(file) = config.encryptedStorageImage.as_ref() {
571         Some(clone_file(file)?)
572     } else {
573         None
574     };
575 
576     let vm_payload_config = match &config.payload {
577         Payload::ConfigPath(config_path) => {
578             load_vm_payload_config_from_file(&apk_file, config_path.as_str())
579                 .with_context(|| format!("Couldn't read config from {}", config_path))?
580         }
581         Payload::PayloadConfig(payload_config) => create_vm_payload_config(payload_config)?,
582     };
583 
584     // For now, the only supported OS is Microdroid
585     let os_name = vm_payload_config.os.name.as_str();
586     if os_name != MICRODROID_OS_NAME {
587         bail!("Unknown OS \"{}\"", os_name);
588     }
589 
590     // It is safe to construct a filename based on the os_name because we've already checked that it
591     // is one of the allowed values.
592     let vm_config_path = PathBuf::from(format!("/apex/com.android.virt/etc/{}.json", os_name));
593     let vm_config_file = File::open(vm_config_path)?;
594     let mut vm_config = VmConfig::load(&vm_config_file)?.to_parcelable()?;
595 
596     if config.memoryMib > 0 {
597         vm_config.memoryMib = config.memoryMib;
598     }
599 
600     vm_config.name = config.name.clone();
601     vm_config.protectedVm = config.protectedVm;
602     vm_config.cpuTopology = config.cpuTopology;
603     vm_config.taskProfiles = config.taskProfiles.clone();
604     vm_config.gdbPort = config.gdbPort;
605 
606     // Microdroid takes additional init ramdisk & (optionally) storage image
607     add_microdroid_system_images(config, instance_file, storage_image, &mut vm_config)?;
608 
609     // Include Microdroid payload disk (contains apks, idsigs) in vm config
610     add_microdroid_payload_images(
611         config,
612         debug_config,
613         temporary_directory,
614         apk_file,
615         idsig_file,
616         &vm_payload_config,
617         &mut vm_config,
618     )?;
619 
620     Ok(vm_config)
621 }
622 
load_vm_payload_config_from_file(apk_file: &File, config_path: &str) -> Result<VmPayloadConfig>623 fn load_vm_payload_config_from_file(apk_file: &File, config_path: &str) -> Result<VmPayloadConfig> {
624     let mut apk_zip = ZipArchive::new(apk_file)?;
625     let config_file = apk_zip.by_name(config_path)?;
626     Ok(serde_json::from_reader(config_file)?)
627 }
628 
create_vm_payload_config( payload_config: &VirtualMachinePayloadConfig, ) -> Result<VmPayloadConfig>629 fn create_vm_payload_config(
630     payload_config: &VirtualMachinePayloadConfig,
631 ) -> Result<VmPayloadConfig> {
632     // There isn't an actual config file. Construct a synthetic VmPayloadConfig from the explicit
633     // parameters we've been given. Microdroid will do something equivalent inside the VM using the
634     // payload config that we send it via the metadata file.
635 
636     let payload_binary_name = &payload_config.payloadBinaryName;
637     if payload_binary_name.contains('/') {
638         bail!("Payload binary name must not specify a path: {payload_binary_name}");
639     }
640 
641     let task = Task { type_: TaskType::MicrodroidLauncher, command: payload_binary_name.clone() };
642     Ok(VmPayloadConfig {
643         os: OsConfig { name: MICRODROID_OS_NAME.to_owned() },
644         task: Some(task),
645         apexes: vec![],
646         extra_apks: vec![],
647         prefer_staged: false,
648         export_tombstones: None,
649         enable_authfs: false,
650     })
651 }
652 
653 /// Generates a unique filename to use for a composite disk image.
make_composite_image_filenames( temporary_directory: &Path, next_temporary_image_id: &mut u64, ) -> CompositeImageFilenames654 fn make_composite_image_filenames(
655     temporary_directory: &Path,
656     next_temporary_image_id: &mut u64,
657 ) -> CompositeImageFilenames {
658     let id = *next_temporary_image_id;
659     *next_temporary_image_id += 1;
660     CompositeImageFilenames {
661         composite: temporary_directory.join(format!("composite-{}.img", id)),
662         header: temporary_directory.join(format!("composite-{}-header.img", id)),
663         footer: temporary_directory.join(format!("composite-{}-footer.img", id)),
664     }
665 }
666 
667 /// Filenames for a composite disk image, including header and footer partitions.
668 #[derive(Clone, Debug, Eq, PartialEq)]
669 struct CompositeImageFilenames {
670     /// The composite disk image itself.
671     composite: PathBuf,
672     /// The header partition image.
673     header: PathBuf,
674     /// The footer partition image.
675     footer: PathBuf,
676 }
677 
678 /// Checks whether the caller has a specific permission
check_permission(perm: &str) -> binder::Result<()>679 fn check_permission(perm: &str) -> binder::Result<()> {
680     let calling_pid = get_calling_pid();
681     let calling_uid = get_calling_uid();
682     // Root can do anything
683     if calling_uid == 0 {
684         return Ok(());
685     }
686     let perm_svc: Strong<dyn IPermissionController::IPermissionController> =
687         binder::get_interface("permission")?;
688     if perm_svc.checkPermission(perm, calling_pid, calling_uid as i32)? {
689         Ok(())
690     } else {
691         Err(Status::new_exception_str(
692             ExceptionCode::SECURITY,
693             Some(format!("does not have the {} permission", perm)),
694         ))
695     }
696 }
697 
698 /// Check whether the caller of the current Binder method is allowed to manage VMs
check_manage_access() -> binder::Result<()>699 fn check_manage_access() -> binder::Result<()> {
700     check_permission("android.permission.MANAGE_VIRTUAL_MACHINE")
701 }
702 
703 /// Check whether the caller of the current Binder method is allowed to create custom VMs
check_use_custom_virtual_machine() -> binder::Result<()>704 fn check_use_custom_virtual_machine() -> binder::Result<()> {
705     check_permission("android.permission.USE_CUSTOM_VIRTUAL_MACHINE")
706 }
707 
708 /// Return whether a partition is exempt from selinux label checks, because we know that it does
709 /// not contain code and is likely to be generated in an app-writable directory.
is_safe_app_partition(label: &str) -> bool710 fn is_safe_app_partition(label: &str) -> bool {
711     // See add_microdroid_system_images & add_microdroid_payload_images in payload.rs.
712     label == "vm-instance"
713         || label == "encryptedstore"
714         || label == "microdroid-apk-idsig"
715         || label == "payload-metadata"
716         || label.starts_with("extra-idsig-")
717 }
718 
719 /// Check that a file SELinux label is acceptable.
720 ///
721 /// We only want to allow code in a VM to be sourced from places that apps, and the
722 /// system, do not have write access to.
723 ///
724 /// Note that sepolicy must also grant read access for these types to both virtualization
725 /// service and crosvm.
726 ///
727 /// App private data files are deliberately excluded, to avoid arbitrary payloads being run on
728 /// user devices (W^X).
check_label_is_allowed(context: &SeContext) -> Result<()>729 fn check_label_is_allowed(context: &SeContext) -> Result<()> {
730     match context.selinux_type()? {
731         | "apk_data_file" // APKs of an installed app
732         | "shell_data_file" // test files created via adb shell
733         | "staging_data_file" // updated/staged APEX images
734         | "system_file" // immutable dm-verity protected partition
735         | "virtualizationservice_data_file" // files created by VS / VirtMgr
736          => Ok(()),
737         _ => bail!("Label {} is not allowed", context),
738     }
739 }
740 
check_label_for_partition(partition: &Partition) -> Result<()>741 fn check_label_for_partition(partition: &Partition) -> Result<()> {
742     let file = partition.image.as_ref().unwrap().as_ref();
743     check_label_is_allowed(&getfilecon(file)?)
744         .with_context(|| format!("Partition {} invalid", &partition.label))
745 }
746 
check_label_for_kernel_files(kernel: &Option<File>, initrd: &Option<File>) -> Result<()>747 fn check_label_for_kernel_files(kernel: &Option<File>, initrd: &Option<File>) -> Result<()> {
748     if let Some(f) = kernel {
749         check_label_for_file(f, "kernel")?;
750     }
751     if let Some(f) = initrd {
752         check_label_for_file(f, "initrd")?;
753     }
754     Ok(())
755 }
check_label_for_file(file: &File, name: &str) -> Result<()>756 fn check_label_for_file(file: &File, name: &str) -> Result<()> {
757     check_label_is_allowed(&getfilecon(file)?).with_context(|| format!("{} file invalid", name))
758 }
759 
760 /// Implementation of the AIDL `IVirtualMachine` interface. Used as a handle to a VM.
761 #[derive(Debug)]
762 struct VirtualMachine {
763     instance: Arc<VmInstance>,
764 }
765 
766 impl VirtualMachine {
create(instance: Arc<VmInstance>) -> Strong<dyn IVirtualMachine>767     fn create(instance: Arc<VmInstance>) -> Strong<dyn IVirtualMachine> {
768         BnVirtualMachine::new_binder(VirtualMachine { instance }, BinderFeatures::default())
769     }
770 }
771 
772 impl Interface for VirtualMachine {}
773 
774 impl IVirtualMachine for VirtualMachine {
getCid(&self) -> binder::Result<i32>775     fn getCid(&self) -> binder::Result<i32> {
776         // Don't check permission. The owner of the VM might have passed this binder object to
777         // others.
778         Ok(self.instance.cid as i32)
779     }
780 
getState(&self) -> binder::Result<VirtualMachineState>781     fn getState(&self) -> binder::Result<VirtualMachineState> {
782         // Don't check permission. The owner of the VM might have passed this binder object to
783         // others.
784         Ok(get_state(&self.instance))
785     }
786 
registerCallback( &self, callback: &Strong<dyn IVirtualMachineCallback>, ) -> binder::Result<()>787     fn registerCallback(
788         &self,
789         callback: &Strong<dyn IVirtualMachineCallback>,
790     ) -> binder::Result<()> {
791         // Don't check permission. The owner of the VM might have passed this binder object to
792         // others.
793         //
794         // TODO: Should this give an error if the VM is already dead?
795         self.instance.callbacks.add(callback.clone());
796         Ok(())
797     }
798 
start(&self) -> binder::Result<()>799     fn start(&self) -> binder::Result<()> {
800         self.instance.start().map_err(|e| {
801             error!("Error starting VM with CID {}: {:?}", self.instance.cid, e);
802             Status::new_service_specific_error_str(-1, Some(e.to_string()))
803         })
804     }
805 
stop(&self) -> binder::Result<()>806     fn stop(&self) -> binder::Result<()> {
807         self.instance.kill().map_err(|e| {
808             error!("Error stopping VM with CID {}: {:?}", self.instance.cid, e);
809             Status::new_service_specific_error_str(-1, Some(e.to_string()))
810         })
811     }
812 
onTrimMemory(&self, level: MemoryTrimLevel) -> binder::Result<()>813     fn onTrimMemory(&self, level: MemoryTrimLevel) -> binder::Result<()> {
814         self.instance.trim_memory(level).map_err(|e| {
815             error!("Error trimming VM with CID {}: {:?}", self.instance.cid, e);
816             Status::new_service_specific_error_str(-1, Some(e.to_string()))
817         })
818     }
819 
connectVsock(&self, port: i32) -> binder::Result<ParcelFileDescriptor>820     fn connectVsock(&self, port: i32) -> binder::Result<ParcelFileDescriptor> {
821         if !matches!(&*self.instance.vm_state.lock().unwrap(), VmState::Running { .. }) {
822             return Err(Status::new_service_specific_error_str(-1, Some("VM is not running")));
823         }
824         let port = port as u32;
825         if port < 1024 {
826             return Err(Status::new_service_specific_error_str(
827                 -1,
828                 Some(format!("Can't connect to privileged port {port}")),
829             ));
830         }
831         let stream = VsockStream::connect_with_cid_port(self.instance.cid, port).map_err(|e| {
832             Status::new_service_specific_error_str(-1, Some(format!("Failed to connect: {:?}", e)))
833         })?;
834         Ok(vsock_stream_to_pfd(stream))
835     }
836 }
837 
838 impl Drop for VirtualMachine {
drop(&mut self)839     fn drop(&mut self) {
840         debug!("Dropping {:?}", self);
841         if let Err(e) = self.instance.kill() {
842             debug!("Error stopping dropped VM with CID {}: {:?}", self.instance.cid, e);
843         }
844     }
845 }
846 
847 /// A set of Binders to be called back in response to various events on the VM, such as when it
848 /// dies.
849 #[derive(Debug, Default)]
850 pub struct VirtualMachineCallbacks(Mutex<Vec<Strong<dyn IVirtualMachineCallback>>>);
851 
852 impl VirtualMachineCallbacks {
853     /// Call all registered callbacks to notify that the payload has started.
notify_payload_started(&self, cid: Cid)854     pub fn notify_payload_started(&self, cid: Cid) {
855         let callbacks = &*self.0.lock().unwrap();
856         for callback in callbacks {
857             if let Err(e) = callback.onPayloadStarted(cid as i32) {
858                 error!("Error notifying payload start event from VM CID {}: {:?}", cid, e);
859             }
860         }
861     }
862 
863     /// Call all registered callbacks to notify that the payload is ready to serve.
notify_payload_ready(&self, cid: Cid)864     pub fn notify_payload_ready(&self, cid: Cid) {
865         let callbacks = &*self.0.lock().unwrap();
866         for callback in callbacks {
867             if let Err(e) = callback.onPayloadReady(cid as i32) {
868                 error!("Error notifying payload ready event from VM CID {}: {:?}", cid, e);
869             }
870         }
871     }
872 
873     /// Call all registered callbacks to notify that the payload has finished.
notify_payload_finished(&self, cid: Cid, exit_code: i32)874     pub fn notify_payload_finished(&self, cid: Cid, exit_code: i32) {
875         let callbacks = &*self.0.lock().unwrap();
876         for callback in callbacks {
877             if let Err(e) = callback.onPayloadFinished(cid as i32, exit_code) {
878                 error!("Error notifying payload finish event from VM CID {}: {:?}", cid, e);
879             }
880         }
881     }
882 
883     /// Call all registered callbacks to say that the VM encountered an error.
notify_error(&self, cid: Cid, error_code: ErrorCode, message: &str)884     pub fn notify_error(&self, cid: Cid, error_code: ErrorCode, message: &str) {
885         let callbacks = &*self.0.lock().unwrap();
886         for callback in callbacks {
887             if let Err(e) = callback.onError(cid as i32, error_code, message) {
888                 error!("Error notifying error event from VM CID {}: {:?}", cid, e);
889             }
890         }
891     }
892 
893     /// Call all registered callbacks to say that the VM has died.
callback_on_died(&self, cid: Cid, reason: DeathReason)894     pub fn callback_on_died(&self, cid: Cid, reason: DeathReason) {
895         let callbacks = &*self.0.lock().unwrap();
896         for callback in callbacks {
897             if let Err(e) = callback.onDied(cid as i32, reason) {
898                 error!("Error notifying exit of VM CID {}: {:?}", cid, e);
899             }
900         }
901     }
902 
903     /// Add a new callback to the set.
add(&self, callback: Strong<dyn IVirtualMachineCallback>)904     fn add(&self, callback: Strong<dyn IVirtualMachineCallback>) {
905         self.0.lock().unwrap().push(callback);
906     }
907 }
908 
909 /// The mutable state of the VirtualizationService. There should only be one instance of this
910 /// struct.
911 #[derive(Debug, Default)]
912 struct State {
913     /// The VMs which have been started. When VMs are started a weak reference is added to this list
914     /// while a strong reference is returned to the caller over Binder. Once all copies of the
915     /// Binder client are dropped the weak reference here will become invalid, and will be removed
916     /// from the list opportunistically the next time `add_vm` is called.
917     vms: Vec<Weak<VmInstance>>,
918 }
919 
920 impl State {
921     /// Get a list of VMs which still have Binder references to them.
vms(&self) -> Vec<Arc<VmInstance>>922     fn vms(&self) -> Vec<Arc<VmInstance>> {
923         // Attempt to upgrade the weak pointers to strong pointers.
924         self.vms.iter().filter_map(Weak::upgrade).collect()
925     }
926 
927     /// Add a new VM to the list.
add_vm(&mut self, vm: Weak<VmInstance>)928     fn add_vm(&mut self, vm: Weak<VmInstance>) {
929         // Garbage collect any entries from the stored list which no longer exist.
930         self.vms.retain(|vm| vm.strong_count() > 0);
931 
932         // Actually add the new VM.
933         self.vms.push(vm);
934     }
935 
936     /// Get a VM that corresponds to the given cid
get_vm(&self, cid: Cid) -> Option<Arc<VmInstance>>937     fn get_vm(&self, cid: Cid) -> Option<Arc<VmInstance>> {
938         self.vms().into_iter().find(|vm| vm.cid == cid)
939     }
940 }
941 
942 /// Gets the `VirtualMachineState` of the given `VmInstance`.
get_state(instance: &VmInstance) -> VirtualMachineState943 fn get_state(instance: &VmInstance) -> VirtualMachineState {
944     match &*instance.vm_state.lock().unwrap() {
945         VmState::NotStarted { .. } => VirtualMachineState::NOT_STARTED,
946         VmState::Running { .. } => match instance.payload_state() {
947             PayloadState::Starting => VirtualMachineState::STARTING,
948             PayloadState::Started => VirtualMachineState::STARTED,
949             PayloadState::Ready => VirtualMachineState::READY,
950             PayloadState::Finished => VirtualMachineState::FINISHED,
951             PayloadState::Hangup => VirtualMachineState::DEAD,
952         },
953         VmState::Dead => VirtualMachineState::DEAD,
954         VmState::Failed => VirtualMachineState::DEAD,
955     }
956 }
957 
958 /// Converts a `&ParcelFileDescriptor` to a `File` by cloning the file.
clone_file(file: &ParcelFileDescriptor) -> Result<File, Status>959 pub fn clone_file(file: &ParcelFileDescriptor) -> Result<File, Status> {
960     file.as_ref().try_clone().map_err(|e| {
961         Status::new_exception_str(
962             ExceptionCode::BAD_PARCELABLE,
963             Some(format!("Failed to clone File from ParcelFileDescriptor: {:?}", e)),
964         )
965     })
966 }
967 
968 /// Converts an `&Option<ParcelFileDescriptor>` to an `Option<File>` by cloning the file.
maybe_clone_file(file: &Option<ParcelFileDescriptor>) -> Result<Option<File>, Status>969 fn maybe_clone_file(file: &Option<ParcelFileDescriptor>) -> Result<Option<File>, Status> {
970     file.as_ref().map(clone_file).transpose()
971 }
972 
973 /// Converts a `VsockStream` to a `ParcelFileDescriptor`.
vsock_stream_to_pfd(stream: VsockStream) -> ParcelFileDescriptor974 fn vsock_stream_to_pfd(stream: VsockStream) -> ParcelFileDescriptor {
975     // SAFETY: ownership is transferred from stream to f
976     let f = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
977     ParcelFileDescriptor::new(f)
978 }
979 
980 /// Parses the platform version requirement string.
parse_platform_version_req(s: &str) -> Result<VersionReq, Status>981 fn parse_platform_version_req(s: &str) -> Result<VersionReq, Status> {
982     VersionReq::parse(s).map_err(|e| {
983         Status::new_exception_str(
984             ExceptionCode::BAD_PARCELABLE,
985             Some(format!("Invalid platform version requirement {}: {:?}", s, e)),
986         )
987     })
988 }
989 
990 /// Create the empty ramdump file
prepare_ramdump_file(temporary_directory: &Path) -> binder::Result<File>991 fn prepare_ramdump_file(temporary_directory: &Path) -> binder::Result<File> {
992     // `ramdump_write` is sent to crosvm and will be the backing store for the /dev/hvc1 where
993     // VM will emit ramdump to. `ramdump_read` will be sent back to the client (i.e. the VM
994     // owner) for readout.
995     let ramdump_path = temporary_directory.join("ramdump");
996     let ramdump = File::create(ramdump_path).map_err(|e| {
997         error!("Failed to prepare ramdump file: {:?}", e);
998         Status::new_service_specific_error_str(
999             -1,
1000             Some(format!("Failed to prepare ramdump file: {:?}", e)),
1001         )
1002     })?;
1003     Ok(ramdump)
1004 }
1005 
is_protected(config: &VirtualMachineConfig) -> bool1006 fn is_protected(config: &VirtualMachineConfig) -> bool {
1007     match config {
1008         VirtualMachineConfig::RawConfig(config) => config.protectedVm,
1009         VirtualMachineConfig::AppConfig(config) => config.protectedVm,
1010     }
1011 }
1012 
check_gdb_allowed(config: &VirtualMachineConfig) -> binder::Result<()>1013 fn check_gdb_allowed(config: &VirtualMachineConfig) -> binder::Result<()> {
1014     if is_protected(config) {
1015         return Err(Status::new_exception_str(
1016             ExceptionCode::SECURITY,
1017             Some("can't use gdb with protected VMs"),
1018         ));
1019     }
1020 
1021     match config {
1022         VirtualMachineConfig::RawConfig(_) => Ok(()),
1023         VirtualMachineConfig::AppConfig(config) => {
1024             if config.debugLevel != DebugLevel::FULL {
1025                 Err(Status::new_exception_str(
1026                     ExceptionCode::SECURITY,
1027                     Some("can't use gdb with non-debuggable VMs"),
1028                 ))
1029             } else {
1030                 Ok(())
1031             }
1032         }
1033     }
1034 }
1035 
extract_gdb_port(config: &VirtualMachineConfig) -> Option<NonZeroU16>1036 fn extract_gdb_port(config: &VirtualMachineConfig) -> Option<NonZeroU16> {
1037     match config {
1038         VirtualMachineConfig::RawConfig(config) => NonZeroU16::new(config.gdbPort as u16),
1039         VirtualMachineConfig::AppConfig(config) => NonZeroU16::new(config.gdbPort as u16),
1040     }
1041 }
1042 
clone_or_prepare_logger_fd( debug_config: &DebugConfig, fd: Option<&ParcelFileDescriptor>, tag: String, ) -> Result<Option<File>, Status>1043 fn clone_or_prepare_logger_fd(
1044     debug_config: &DebugConfig,
1045     fd: Option<&ParcelFileDescriptor>,
1046     tag: String,
1047 ) -> Result<Option<File>, Status> {
1048     if let Some(fd) = fd {
1049         return Ok(Some(clone_file(fd)?));
1050     }
1051 
1052     if !debug_config.should_prepare_console_output() {
1053         return Ok(None);
1054     };
1055 
1056     let (raw_read_fd, raw_write_fd) = pipe().map_err(|e| {
1057         Status::new_service_specific_error_str(-1, Some(format!("Failed to create pipe: {:?}", e)))
1058     })?;
1059 
1060     // SAFETY: We are the sole owners of these fds as they were just created.
1061     let mut reader = BufReader::new(unsafe { File::from_raw_fd(raw_read_fd) });
1062     let write_fd = unsafe { File::from_raw_fd(raw_write_fd) };
1063 
1064     std::thread::spawn(move || loop {
1065         let mut buf = vec![];
1066         match reader.read_until(b'\n', &mut buf) {
1067             Ok(0) => {
1068                 // EOF
1069                 return;
1070             }
1071             Ok(size) => {
1072                 if buf[size - 1] == b'\n' {
1073                     buf.pop();
1074                 }
1075                 info!("{}: {}", &tag, &String::from_utf8_lossy(&buf));
1076             }
1077             Err(e) => {
1078                 error!("Could not read console pipe: {:?}", e);
1079                 return;
1080             }
1081         };
1082     });
1083 
1084     Ok(Some(write_fd))
1085 }
1086 
1087 /// Simple utility for referencing Borrowed or Owned. Similar to std::borrow::Cow, but
1088 /// it doesn't require that T implements Clone.
1089 enum BorrowedOrOwned<'a, T> {
1090     Borrowed(&'a T),
1091     Owned(T),
1092 }
1093 
1094 impl<'a, T> AsRef<T> for BorrowedOrOwned<'a, T> {
as_ref(&self) -> &T1095     fn as_ref(&self) -> &T {
1096         match self {
1097             Self::Borrowed(b) => b,
1098             Self::Owned(o) => o,
1099         }
1100     }
1101 }
1102 
1103 /// Implementation of `IVirtualMachineService`, the entry point of the AIDL service.
1104 #[derive(Debug, Default)]
1105 struct VirtualMachineService {
1106     state: Arc<Mutex<State>>,
1107     cid: Cid,
1108 }
1109 
1110 impl Interface for VirtualMachineService {}
1111 
1112 impl IVirtualMachineService for VirtualMachineService {
notifyPayloadStarted(&self) -> binder::Result<()>1113     fn notifyPayloadStarted(&self) -> binder::Result<()> {
1114         let cid = self.cid;
1115         if let Some(vm) = self.state.lock().unwrap().get_vm(cid) {
1116             info!("VM with CID {} started payload", cid);
1117             vm.update_payload_state(PayloadState::Started).map_err(|e| {
1118                 Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some(e.to_string()))
1119             })?;
1120             vm.callbacks.notify_payload_started(cid);
1121 
1122             let vm_start_timestamp = vm.vm_metric.lock().unwrap().start_timestamp;
1123             write_vm_booted_stats(vm.requester_uid as i32, &vm.name, vm_start_timestamp);
1124             Ok(())
1125         } else {
1126             error!("notifyPayloadStarted is called from an unknown CID {}", cid);
1127             Err(Status::new_service_specific_error_str(
1128                 -1,
1129                 Some(format!("cannot find a VM with CID {}", cid)),
1130             ))
1131         }
1132     }
1133 
notifyPayloadReady(&self) -> binder::Result<()>1134     fn notifyPayloadReady(&self) -> binder::Result<()> {
1135         let cid = self.cid;
1136         if let Some(vm) = self.state.lock().unwrap().get_vm(cid) {
1137             info!("VM with CID {} reported payload is ready", cid);
1138             vm.update_payload_state(PayloadState::Ready).map_err(|e| {
1139                 Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some(e.to_string()))
1140             })?;
1141             vm.callbacks.notify_payload_ready(cid);
1142             Ok(())
1143         } else {
1144             error!("notifyPayloadReady is called from an unknown CID {}", cid);
1145             Err(Status::new_service_specific_error_str(
1146                 -1,
1147                 Some(format!("cannot find a VM with CID {}", cid)),
1148             ))
1149         }
1150     }
1151 
notifyPayloadFinished(&self, exit_code: i32) -> binder::Result<()>1152     fn notifyPayloadFinished(&self, exit_code: i32) -> binder::Result<()> {
1153         let cid = self.cid;
1154         if let Some(vm) = self.state.lock().unwrap().get_vm(cid) {
1155             info!("VM with CID {} finished payload", cid);
1156             vm.update_payload_state(PayloadState::Finished).map_err(|e| {
1157                 Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some(e.to_string()))
1158             })?;
1159             vm.callbacks.notify_payload_finished(cid, exit_code);
1160             Ok(())
1161         } else {
1162             error!("notifyPayloadFinished is called from an unknown CID {}", cid);
1163             Err(Status::new_service_specific_error_str(
1164                 -1,
1165                 Some(format!("cannot find a VM with CID {}", cid)),
1166             ))
1167         }
1168     }
1169 
notifyError(&self, error_code: ErrorCode, message: &str) -> binder::Result<()>1170     fn notifyError(&self, error_code: ErrorCode, message: &str) -> binder::Result<()> {
1171         let cid = self.cid;
1172         if let Some(vm) = self.state.lock().unwrap().get_vm(cid) {
1173             info!("VM with CID {} encountered an error", cid);
1174             vm.update_payload_state(PayloadState::Finished).map_err(|e| {
1175                 Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some(e.to_string()))
1176             })?;
1177             vm.callbacks.notify_error(cid, error_code, message);
1178             Ok(())
1179         } else {
1180             error!("notifyError is called from an unknown CID {}", cid);
1181             Err(Status::new_service_specific_error_str(
1182                 -1,
1183                 Some(format!("cannot find a VM with CID {}", cid)),
1184             ))
1185         }
1186     }
1187 }
1188 
1189 impl VirtualMachineService {
new_binder(state: Arc<Mutex<State>>, cid: Cid) -> Strong<dyn IVirtualMachineService>1190     fn new_binder(state: Arc<Mutex<State>>, cid: Cid) -> Strong<dyn IVirtualMachineService> {
1191         BnVirtualMachineService::new_binder(
1192             VirtualMachineService { state, cid },
1193             BinderFeatures::default(),
1194         )
1195     }
1196 }
1197 
1198 #[cfg(test)]
1199 mod tests {
1200     use super::*;
1201 
1202     #[test]
test_is_allowed_label_for_partition() -> Result<()>1203     fn test_is_allowed_label_for_partition() -> Result<()> {
1204         let expected_results = vec![
1205             ("u:object_r:system_file:s0", true),
1206             ("u:object_r:apk_data_file:s0", true),
1207             ("u:object_r:app_data_file:s0", false),
1208             ("u:object_r:app_data_file:s0:c512,c768", false),
1209             ("u:object_r:privapp_data_file:s0:c512,c768", false),
1210             ("invalid", false),
1211             ("user:role:apk_data_file:severity:categories", true),
1212             ("user:role:apk_data_file:severity:categories:extraneous", false),
1213         ];
1214 
1215         for (label, expected_valid) in expected_results {
1216             let context = SeContext::new(label)?;
1217             let result = check_label_is_allowed(&context);
1218             if expected_valid {
1219                 assert!(result.is_ok(), "Expected label {} to be allowed, got {:?}", label, result);
1220             } else if result.is_ok() {
1221                 bail!("Expected label {} to be disallowed", label);
1222             }
1223         }
1224         Ok(())
1225     }
1226 
1227     #[test]
test_create_or_update_idsig_file_empty_apk() -> Result<()>1228     fn test_create_or_update_idsig_file_empty_apk() -> Result<()> {
1229         let apk = tempfile::tempfile().unwrap();
1230         let idsig = tempfile::tempfile().unwrap();
1231 
1232         let ret = create_or_update_idsig_file(
1233             &ParcelFileDescriptor::new(apk),
1234             &ParcelFileDescriptor::new(idsig),
1235         );
1236         assert!(ret.is_err(), "should fail");
1237         Ok(())
1238     }
1239 
1240     #[test]
test_create_or_update_idsig_dir_instead_of_file_for_apk() -> Result<()>1241     fn test_create_or_update_idsig_dir_instead_of_file_for_apk() -> Result<()> {
1242         let tmp_dir = tempfile::TempDir::new().unwrap();
1243         let apk = File::open(tmp_dir.path()).unwrap();
1244         let idsig = tempfile::tempfile().unwrap();
1245 
1246         let ret = create_or_update_idsig_file(
1247             &ParcelFileDescriptor::new(apk),
1248             &ParcelFileDescriptor::new(idsig),
1249         );
1250         assert!(ret.is_err(), "should fail");
1251         Ok(())
1252     }
1253 
1254     /// Verifies that create_or_update_idsig_file won't oom if a fd that corresponds to a directory
1255     /// on ext4 filesystem is passed.
1256     /// On ext4 lseek on a directory fd will return (off_t)-1 (see:
1257     /// https://bugzilla.kernel.org/show_bug.cgi?id=200043), which will result in
1258     /// create_or_update_idsig_file ooming while attempting to allocate petabytes of memory.
1259     #[test]
test_create_or_update_idsig_does_not_crash_dir_on_ext4() -> Result<()>1260     fn test_create_or_update_idsig_does_not_crash_dir_on_ext4() -> Result<()> {
1261         // APEXes are backed by the ext4.
1262         let apk = File::open("/apex/com.android.virt/").unwrap();
1263         let idsig = tempfile::tempfile().unwrap();
1264 
1265         let ret = create_or_update_idsig_file(
1266             &ParcelFileDescriptor::new(apk),
1267             &ParcelFileDescriptor::new(idsig),
1268         );
1269         assert!(ret.is_err(), "should fail");
1270         Ok(())
1271     }
1272 }
1273