blob: 858659726ed2596df41eb6e6ec6a2e7a2cf8811e [file] [log] [blame]
David Brazdilafc9a9e2023-01-12 16:08:10 +00001// 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
17use crate::{get_calling_pid, get_calling_uid};
David Brazdil33a31022023-01-12 16:55:16 +000018use crate::atom::{forward_vm_booted_atom, forward_vm_creation_atom, forward_vm_exited_atom};
Alice Wangc2fec932023-02-23 16:24:02 +000019use crate::rkpvm::request_certificate;
David Brazdilafc9a9e2023-01-12 16:08:10 +000020use android_os_permissions_aidl::aidl::android::os::IPermissionController;
Alice Wangc2fec932023-02-23 16:24:02 +000021use android_system_virtualizationservice::{
Inseob Kim53d0b212023-07-20 16:58:37 +090022 aidl::android::system::virtualizationservice::AssignableDevice::AssignableDevice,
Alice Wangc2fec932023-02-23 16:24:02 +000023 aidl::android::system::virtualizationservice::VirtualMachineDebugInfo::VirtualMachineDebugInfo,
24 binder::ParcelFileDescriptor,
25};
David Brazdilafc9a9e2023-01-12 16:08:10 +000026use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::{
27 AtomVmBooted::AtomVmBooted,
28 AtomVmCreationRequested::AtomVmCreationRequested,
29 AtomVmExited::AtomVmExited,
30 IGlobalVmContext::{BnGlobalVmContext, IGlobalVmContext},
31 IVirtualizationServiceInternal::IVirtualizationServiceInternal,
Inseob Kimbdca0472023-07-28 19:20:56 +090032 IVfioHandler::{BpVfioHandler, IVfioHandler},
David Brazdilafc9a9e2023-01-12 16:08:10 +000033};
34use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::VM_TOMBSTONES_SERVICE_PORT;
Alice Wangd1b11a02023-04-18 12:30:20 +000035use anyhow::{anyhow, ensure, Context, Result};
Jiyong Parkd7bd2f22023-08-10 20:41:19 +090036use avflog::LogResult;
Jiyong Park2227eaa2023-08-04 11:59:18 +090037use binder::{self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, LazyServiceGuard, Status, Strong, IntoBinderResult};
David Brazdilafc9a9e2023-01-12 16:08:10 +000038use libc::VMADDR_CID_HOST;
39use log::{error, info, warn};
40use rustutils::system_properties;
Inseob Kimc4a774d2023-08-30 12:48:43 +090041use serde::Deserialize;
42use std::collections::{HashMap, HashSet};
43use std::fs::{self, create_dir, remove_dir_all, set_permissions, Permissions};
David Brazdilafc9a9e2023-01-12 16:08:10 +000044use std::io::{Read, Write};
45use std::os::unix::fs::PermissionsExt;
46use std::os::unix::raw::{pid_t, uid_t};
Inseob Kim55438b22023-08-09 20:16:01 +090047use std::path::{Path, PathBuf};
David Brazdilafc9a9e2023-01-12 16:08:10 +000048use std::sync::{Arc, Mutex, Weak};
49use tombstoned_client::{DebuggerdDumpType, TombstonedConnection};
50use vsock::{VsockListener, VsockStream};
Inseob Kimbdca0472023-07-28 19:20:56 +090051use nix::unistd::{chown, Uid};
David Brazdilafc9a9e2023-01-12 16:08:10 +000052
53/// The unique ID of a VM used (together with a port number) for vsock communication.
54pub type Cid = u32;
55
56pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
57
58/// Directory in which to write disk image files used while running VMs.
59pub const TEMPORARY_DIRECTORY: &str = "/data/misc/virtualizationservice";
60
61/// The first CID to assign to a guest VM managed by the VirtualizationService. CIDs lower than this
62/// are reserved for the host or other usage.
63const GUEST_CID_MIN: Cid = 2048;
64const GUEST_CID_MAX: Cid = 65535;
65
66const SYSPROP_LAST_CID: &str = "virtualizationservice.state.last_cid";
67
68const CHUNK_RECV_MAX_LEN: usize = 1024;
69
70fn is_valid_guest_cid(cid: Cid) -> bool {
71 (GUEST_CID_MIN..=GUEST_CID_MAX).contains(&cid)
72}
73
74/// Singleton service for allocating globally-unique VM resources, such as the CID, and running
75/// singleton servers, like tombstone receiver.
76#[derive(Debug, Default)]
77pub struct VirtualizationServiceInternal {
78 state: Arc<Mutex<GlobalState>>,
79}
80
81impl VirtualizationServiceInternal {
82 pub fn init() -> VirtualizationServiceInternal {
83 let service = VirtualizationServiceInternal::default();
84
85 std::thread::spawn(|| {
86 if let Err(e) = handle_stream_connection_tombstoned() {
87 warn!("Error receiving tombstone from guest or writing them. Error: {:?}", e);
88 }
89 });
90
91 service
92 }
93}
94
95impl Interface for VirtualizationServiceInternal {}
96
97impl IVirtualizationServiceInternal for VirtualizationServiceInternal {
98 fn removeMemlockRlimit(&self) -> binder::Result<()> {
99 let pid = get_calling_pid();
100 let lim = libc::rlimit { rlim_cur: libc::RLIM_INFINITY, rlim_max: libc::RLIM_INFINITY };
101
Andrew Walbranb58d1b42023-07-07 13:54:49 +0100102 // SAFETY: borrowing the new limit struct only
David Brazdilafc9a9e2023-01-12 16:08:10 +0000103 let ret = unsafe { libc::prlimit(pid, libc::RLIMIT_MEMLOCK, &lim, std::ptr::null_mut()) };
104
105 match ret {
106 0 => Ok(()),
Jiyong Park2227eaa2023-08-04 11:59:18 +0900107 -1 => Err(std::io::Error::last_os_error().into()),
108 n => Err(anyhow!("Unexpected return value from prlimit(): {n}")),
David Brazdilafc9a9e2023-01-12 16:08:10 +0000109 }
Jiyong Park2227eaa2023-08-04 11:59:18 +0900110 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000111 }
112
113 fn allocateGlobalVmContext(
114 &self,
115 requester_debug_pid: i32,
116 ) -> binder::Result<Strong<dyn IGlobalVmContext>> {
117 check_manage_access()?;
118
119 let requester_uid = get_calling_uid();
120 let requester_debug_pid = requester_debug_pid as pid_t;
121 let state = &mut *self.state.lock().unwrap();
Jiyong Park2227eaa2023-08-04 11:59:18 +0900122 state
123 .allocate_vm_context(requester_uid, requester_debug_pid)
124 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000125 }
126
127 fn atomVmBooted(&self, atom: &AtomVmBooted) -> Result<(), Status> {
128 forward_vm_booted_atom(atom);
129 Ok(())
130 }
131
132 fn atomVmCreationRequested(&self, atom: &AtomVmCreationRequested) -> Result<(), Status> {
133 forward_vm_creation_atom(atom);
134 Ok(())
135 }
136
137 fn atomVmExited(&self, atom: &AtomVmExited) -> Result<(), Status> {
138 forward_vm_exited_atom(atom);
139 Ok(())
140 }
141
142 fn debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>> {
143 check_debug_access()?;
144
145 let state = &mut *self.state.lock().unwrap();
146 let cids = state
147 .held_contexts
148 .iter()
149 .filter_map(|(_, inst)| Weak::upgrade(inst))
150 .map(|vm| VirtualMachineDebugInfo {
151 cid: vm.cid as i32,
152 temporaryDirectory: vm.get_temp_dir().to_string_lossy().to_string(),
153 requesterUid: vm.requester_uid as i32,
Charisee96113f32023-01-26 09:00:42 +0000154 requesterPid: vm.requester_debug_pid,
David Brazdilafc9a9e2023-01-12 16:08:10 +0000155 })
156 .collect();
157 Ok(cids)
158 }
Alice Wangc2fec932023-02-23 16:24:02 +0000159
Alice Wangc206b9b2023-08-28 14:13:51 +0000160 fn requestCertificate(&self, csr: &[u8]) -> binder::Result<Vec<u8>> {
Alice Wangc2fec932023-02-23 16:24:02 +0000161 check_manage_access()?;
162 info!("Received csr. Getting certificate...");
Alice Wangc206b9b2023-08-28 14:13:51 +0000163 request_certificate(csr)
Jiyong Park2227eaa2023-08-04 11:59:18 +0900164 .context("Failed to get certificate")
165 .with_log()
166 .or_service_specific_exception(-1)
Alice Wangc2fec932023-02-23 16:24:02 +0000167 }
Inseob Kim53d0b212023-07-20 16:58:37 +0900168
169 fn getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>> {
170 check_use_custom_virtual_machine()?;
171
Inseob Kimc4a774d2023-08-30 12:48:43 +0900172 let mut ret = Vec::new();
173 let xml_path = Path::new("/vendor/etc/avf/assignable_devices.xml");
174 if !xml_path.exists() {
175 return Ok(ret);
Inseob Kim55438b22023-08-09 20:16:01 +0900176 }
Inseob Kimc4a774d2023-08-30 12:48:43 +0900177
178 let xml = fs::read(xml_path)
179 .context("Failed to read assignable_devices.xml")
180 .with_log()
181 .or_service_specific_exception(-1)?;
182
183 let xml = String::from_utf8(xml)
184 .context("assignable_devices.xml is not a valid UTF-8 file")
185 .with_log()
186 .or_service_specific_exception(-1)?;
187
188 let devices: Devices = serde_xml_rs::from_str(&xml)
189 .context("can't parse assignable_devices.xml")
190 .with_log()
191 .or_service_specific_exception(-1)?;
192
193 let mut device_set = HashSet::new();
194
195 for device in devices.device.into_iter() {
196 if device_set.contains(&device.sysfs_path) {
197 warn!("duplicated assignable device {device:?}; ignoring...")
198 } else if Path::new(&device.sysfs_path).exists() {
199 device_set.insert(device.sysfs_path.clone());
200 ret.push(AssignableDevice { kind: device.kind, node: device.sysfs_path });
201 } else {
202 warn!("assignable device {device:?} doesn't exist; ignoring...");
203 }
204 }
205
206 Ok(ret)
Inseob Kim53d0b212023-07-20 16:58:37 +0900207 }
Inseob Kim1ca0f652023-07-20 17:18:12 +0900208
Inseob Kimf36347b2023-08-03 12:52:48 +0900209 fn bindDevicesToVfioDriver(
210 &self,
211 devices: &[String],
212 dtbo: &ParcelFileDescriptor,
213 ) -> binder::Result<()> {
Inseob Kim1ca0f652023-07-20 17:18:12 +0900214 check_use_custom_virtual_machine()?;
215
Inseob Kimbdca0472023-07-28 19:20:56 +0900216 let vfio_service: Strong<dyn IVfioHandler> =
217 wait_for_interface(<BpVfioHandler as IVfioHandler>::get_descriptor())?;
Inseob Kimf36347b2023-08-03 12:52:48 +0900218
219 vfio_service.bindDevicesToVfioDriver(devices, dtbo)?;
220 Ok(())
Inseob Kim1ca0f652023-07-20 17:18:12 +0900221 }
222}
223
Inseob Kimc4a774d2023-08-30 12:48:43 +0900224// KEEP IN SYNC WITH assignable_devices.xsd
225#[derive(Debug, Deserialize)]
226struct Device {
227 kind: String,
228 sysfs_path: String,
229}
230
231#[derive(Debug, Deserialize)]
232struct Devices {
233 device: Vec<Device>,
234}
235
David Brazdilafc9a9e2023-01-12 16:08:10 +0000236#[derive(Debug, Default)]
237struct GlobalVmInstance {
238 /// The unique CID assigned to the VM for vsock communication.
239 cid: Cid,
240 /// UID of the client who requested this VM instance.
241 requester_uid: uid_t,
242 /// PID of the client who requested this VM instance.
243 requester_debug_pid: pid_t,
244}
245
246impl GlobalVmInstance {
247 fn get_temp_dir(&self) -> PathBuf {
248 let cid = self.cid;
249 format!("{TEMPORARY_DIRECTORY}/{cid}").into()
250 }
251}
252
253/// The mutable state of the VirtualizationServiceInternal. There should only be one instance
254/// of this struct.
255#[derive(Debug, Default)]
256struct GlobalState {
257 /// VM contexts currently allocated to running VMs. A CID is never recycled as long
258 /// as there is a strong reference held by a GlobalVmContext.
259 held_contexts: HashMap<Cid, Weak<GlobalVmInstance>>,
260}
261
262impl GlobalState {
263 /// Get the next available CID, or an error if we have run out. The last CID used is stored in
264 /// a system property so that restart of virtualizationservice doesn't reuse CID while the host
265 /// Android is up.
266 fn get_next_available_cid(&mut self) -> Result<Cid> {
267 // Start trying to find a CID from the last used CID + 1. This ensures
268 // that we do not eagerly recycle CIDs. It makes debugging easier but
269 // also means that retrying to allocate a CID, eg. because it is
270 // erroneously occupied by a process, will not recycle the same CID.
271 let last_cid_prop =
272 system_properties::read(SYSPROP_LAST_CID)?.and_then(|val| match val.parse::<Cid>() {
273 Ok(num) => {
274 if is_valid_guest_cid(num) {
275 Some(num)
276 } else {
277 error!("Invalid value '{}' of property '{}'", num, SYSPROP_LAST_CID);
278 None
279 }
280 }
281 Err(_) => {
282 error!("Invalid value '{}' of property '{}'", val, SYSPROP_LAST_CID);
283 None
284 }
285 });
286
287 let first_cid = if let Some(last_cid) = last_cid_prop {
288 if last_cid == GUEST_CID_MAX {
289 GUEST_CID_MIN
290 } else {
291 last_cid + 1
292 }
293 } else {
294 GUEST_CID_MIN
295 };
296
297 let cid = self
298 .find_available_cid(first_cid..=GUEST_CID_MAX)
299 .or_else(|| self.find_available_cid(GUEST_CID_MIN..first_cid))
300 .ok_or_else(|| anyhow!("Could not find an available CID."))?;
301
302 system_properties::write(SYSPROP_LAST_CID, &format!("{}", cid))?;
303 Ok(cid)
304 }
305
306 fn find_available_cid<I>(&self, mut range: I) -> Option<Cid>
307 where
308 I: Iterator<Item = Cid>,
309 {
310 range.find(|cid| !self.held_contexts.contains_key(cid))
311 }
312
313 fn allocate_vm_context(
314 &mut self,
315 requester_uid: uid_t,
316 requester_debug_pid: pid_t,
317 ) -> Result<Strong<dyn IGlobalVmContext>> {
318 // Garbage collect unused VM contexts.
319 self.held_contexts.retain(|_, instance| instance.strong_count() > 0);
320
321 let cid = self.get_next_available_cid()?;
322 let instance = Arc::new(GlobalVmInstance { cid, requester_uid, requester_debug_pid });
323 create_temporary_directory(&instance.get_temp_dir(), requester_uid)?;
324
325 self.held_contexts.insert(cid, Arc::downgrade(&instance));
326 let binder = GlobalVmContext { instance, ..Default::default() };
327 Ok(BnGlobalVmContext::new_binder(binder, BinderFeatures::default()))
328 }
329}
330
331fn create_temporary_directory(path: &PathBuf, requester_uid: uid_t) -> Result<()> {
332 if path.as_path().exists() {
333 remove_temporary_dir(path).unwrap_or_else(|e| {
334 warn!("Could not delete temporary directory {:?}: {}", path, e);
335 });
336 }
337 // Create a directory that is owned by client's UID but system's GID, and permissions 0700.
338 // If the chown() fails, this will leave behind an empty directory that will get removed
339 // at the next attempt, or if virtualizationservice is restarted.
340 create_dir(path).with_context(|| format!("Could not create temporary directory {:?}", path))?;
341 chown(path, Some(Uid::from_raw(requester_uid)), None)
342 .with_context(|| format!("Could not set ownership of temporary directory {:?}", path))?;
343 Ok(())
344}
345
346/// Removes a directory owned by a different user by first changing its owner back
347/// to VirtualizationService.
348pub fn remove_temporary_dir(path: &PathBuf) -> Result<()> {
Alice Wangd1b11a02023-04-18 12:30:20 +0000349 ensure!(path.as_path().is_dir(), "Path {:?} is not a directory", path);
David Brazdilafc9a9e2023-01-12 16:08:10 +0000350 chown(path, Some(Uid::current()), None)?;
351 set_permissions(path, Permissions::from_mode(0o700))?;
Alice Wangd1b11a02023-04-18 12:30:20 +0000352 remove_dir_all(path)?;
David Brazdilafc9a9e2023-01-12 16:08:10 +0000353 Ok(())
354}
355
356/// Implementation of the AIDL `IGlobalVmContext` interface.
357#[derive(Debug, Default)]
358struct GlobalVmContext {
359 /// Strong reference to the context's instance data structure.
360 instance: Arc<GlobalVmInstance>,
361 /// Keeps our service process running as long as this VM context exists.
362 #[allow(dead_code)]
363 lazy_service_guard: LazyServiceGuard,
364}
365
366impl Interface for GlobalVmContext {}
367
368impl IGlobalVmContext for GlobalVmContext {
369 fn getCid(&self) -> binder::Result<i32> {
370 Ok(self.instance.cid as i32)
371 }
372
373 fn getTemporaryDirectory(&self) -> binder::Result<String> {
374 Ok(self.instance.get_temp_dir().to_string_lossy().to_string())
375 }
376}
377
378fn handle_stream_connection_tombstoned() -> Result<()> {
379 // Should not listen for tombstones on a guest VM's port.
380 assert!(!is_valid_guest_cid(VM_TOMBSTONES_SERVICE_PORT as Cid));
381 let listener =
382 VsockListener::bind_with_cid_port(VMADDR_CID_HOST, VM_TOMBSTONES_SERVICE_PORT as Cid)?;
383 for incoming_stream in listener.incoming() {
384 let mut incoming_stream = match incoming_stream {
385 Err(e) => {
386 warn!("invalid incoming connection: {:?}", e);
387 continue;
388 }
389 Ok(s) => s,
390 };
391 std::thread::spawn(move || {
392 if let Err(e) = handle_tombstone(&mut incoming_stream) {
393 error!("Failed to write tombstone- {:?}", e);
394 }
395 });
396 }
397 Ok(())
398}
399
400fn handle_tombstone(stream: &mut VsockStream) -> Result<()> {
401 if let Ok(addr) = stream.peer_addr() {
402 info!("Vsock Stream connected to cid={} for tombstones", addr.cid());
403 }
404 let tb_connection =
405 TombstonedConnection::connect(std::process::id() as i32, DebuggerdDumpType::Tombstone)
406 .context("Failed to connect to tombstoned")?;
407 let mut text_output = tb_connection
408 .text_output
409 .as_ref()
410 .ok_or_else(|| anyhow!("Could not get file to write the tombstones on"))?;
411 let mut num_bytes_read = 0;
412 loop {
413 let mut chunk_recv = [0; CHUNK_RECV_MAX_LEN];
414 let n = stream
415 .read(&mut chunk_recv)
416 .context("Failed to read tombstone data from Vsock stream")?;
417 if n == 0 {
418 break;
419 }
420 num_bytes_read += n;
421 text_output.write_all(&chunk_recv[0..n]).context("Failed to write guests tombstones")?;
422 }
423 info!("Received {} bytes from guest & wrote to tombstone file", num_bytes_read);
424 tb_connection.notify_completion()?;
425 Ok(())
426}
427
428/// Checks whether the caller has a specific permission
429fn check_permission(perm: &str) -> binder::Result<()> {
430 let calling_pid = get_calling_pid();
431 let calling_uid = get_calling_uid();
432 // Root can do anything
433 if calling_uid == 0 {
434 return Ok(());
435 }
436 let perm_svc: Strong<dyn IPermissionController::IPermissionController> =
437 binder::get_interface("permission")?;
438 if perm_svc.checkPermission(perm, calling_pid, calling_uid as i32)? {
439 Ok(())
440 } else {
Jiyong Park2227eaa2023-08-04 11:59:18 +0900441 Err(anyhow!("does not have the {} permission", perm))
442 .or_binder_exception(ExceptionCode::SECURITY)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000443 }
444}
445
446/// Check whether the caller of the current Binder method is allowed to call debug methods.
447fn check_debug_access() -> binder::Result<()> {
448 check_permission("android.permission.DEBUG_VIRTUAL_MACHINE")
449}
450
451/// Check whether the caller of the current Binder method is allowed to manage VMs
452fn check_manage_access() -> binder::Result<()> {
453 check_permission("android.permission.MANAGE_VIRTUAL_MACHINE")
454}
Inseob Kim53d0b212023-07-20 16:58:37 +0900455
456/// Check whether the caller of the current Binder method is allowed to use custom VMs
457fn check_use_custom_virtual_machine() -> binder::Result<()> {
458 check_permission("android.permission.USE_CUSTOM_VIRTUAL_MACHINE")
459}