blob: 6dc54856dcfdb9e42066cd840c594e08c9277264 [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 Park2227eaa2023-08-04 11:59:18 +090036use binder::{self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, LazyServiceGuard, Status, Strong, IntoBinderResult};
David Brazdilafc9a9e2023-01-12 16:08:10 +000037use libc::VMADDR_CID_HOST;
38use log::{error, info, warn};
39use rustutils::system_properties;
Inseob Kim6ef80972023-07-20 17:23:36 +090040use std::collections::HashMap;
Inseob Kimbdca0472023-07-28 19:20:56 +090041use std::fs::{create_dir, remove_dir_all, set_permissions, Permissions};
David Brazdilafc9a9e2023-01-12 16:08:10 +000042use std::io::{Read, Write};
43use std::os::unix::fs::PermissionsExt;
44use std::os::unix::raw::{pid_t, uid_t};
Inseob Kim55438b22023-08-09 20:16:01 +090045use std::path::{Path, PathBuf};
David Brazdilafc9a9e2023-01-12 16:08:10 +000046use std::sync::{Arc, Mutex, Weak};
47use tombstoned_client::{DebuggerdDumpType, TombstonedConnection};
48use vsock::{VsockListener, VsockStream};
Inseob Kimbdca0472023-07-28 19:20:56 +090049use nix::unistd::{chown, Uid};
David Brazdilafc9a9e2023-01-12 16:08:10 +000050
Jiyong Park2227eaa2023-08-04 11:59:18 +090051/// Convenient trait for logging an error while returning it
52trait LogResult<T, E> {
53 fn with_log(self) -> std::result::Result<T, E>;
54}
55
56impl<T, E: std::fmt::Debug> LogResult<T, E> for std::result::Result<T, E> {
57 fn with_log(self) -> std::result::Result<T, E> {
58 self.map_err(|e| {
59 error!("{e:?}");
60 e
61 })
62 }
63}
64
David Brazdilafc9a9e2023-01-12 16:08:10 +000065/// The unique ID of a VM used (together with a port number) for vsock communication.
66pub type Cid = u32;
67
68pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
69
70/// Directory in which to write disk image files used while running VMs.
71pub const TEMPORARY_DIRECTORY: &str = "/data/misc/virtualizationservice";
72
73/// The first CID to assign to a guest VM managed by the VirtualizationService. CIDs lower than this
74/// are reserved for the host or other usage.
75const GUEST_CID_MIN: Cid = 2048;
76const GUEST_CID_MAX: Cid = 65535;
77
78const SYSPROP_LAST_CID: &str = "virtualizationservice.state.last_cid";
79
80const CHUNK_RECV_MAX_LEN: usize = 1024;
81
82fn is_valid_guest_cid(cid: Cid) -> bool {
83 (GUEST_CID_MIN..=GUEST_CID_MAX).contains(&cid)
84}
85
86/// Singleton service for allocating globally-unique VM resources, such as the CID, and running
87/// singleton servers, like tombstone receiver.
88#[derive(Debug, Default)]
89pub struct VirtualizationServiceInternal {
90 state: Arc<Mutex<GlobalState>>,
91}
92
93impl VirtualizationServiceInternal {
94 pub fn init() -> VirtualizationServiceInternal {
95 let service = VirtualizationServiceInternal::default();
96
97 std::thread::spawn(|| {
98 if let Err(e) = handle_stream_connection_tombstoned() {
99 warn!("Error receiving tombstone from guest or writing them. Error: {:?}", e);
100 }
101 });
102
103 service
104 }
105}
106
107impl Interface for VirtualizationServiceInternal {}
108
109impl IVirtualizationServiceInternal for VirtualizationServiceInternal {
110 fn removeMemlockRlimit(&self) -> binder::Result<()> {
111 let pid = get_calling_pid();
112 let lim = libc::rlimit { rlim_cur: libc::RLIM_INFINITY, rlim_max: libc::RLIM_INFINITY };
113
Andrew Walbranb58d1b42023-07-07 13:54:49 +0100114 // SAFETY: borrowing the new limit struct only
David Brazdilafc9a9e2023-01-12 16:08:10 +0000115 let ret = unsafe { libc::prlimit(pid, libc::RLIMIT_MEMLOCK, &lim, std::ptr::null_mut()) };
116
117 match ret {
118 0 => Ok(()),
Jiyong Park2227eaa2023-08-04 11:59:18 +0900119 -1 => Err(std::io::Error::last_os_error().into()),
120 n => Err(anyhow!("Unexpected return value from prlimit(): {n}")),
David Brazdilafc9a9e2023-01-12 16:08:10 +0000121 }
Jiyong Park2227eaa2023-08-04 11:59:18 +0900122 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000123 }
124
125 fn allocateGlobalVmContext(
126 &self,
127 requester_debug_pid: i32,
128 ) -> binder::Result<Strong<dyn IGlobalVmContext>> {
129 check_manage_access()?;
130
131 let requester_uid = get_calling_uid();
132 let requester_debug_pid = requester_debug_pid as pid_t;
133 let state = &mut *self.state.lock().unwrap();
Jiyong Park2227eaa2023-08-04 11:59:18 +0900134 state
135 .allocate_vm_context(requester_uid, requester_debug_pid)
136 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000137 }
138
139 fn atomVmBooted(&self, atom: &AtomVmBooted) -> Result<(), Status> {
140 forward_vm_booted_atom(atom);
141 Ok(())
142 }
143
144 fn atomVmCreationRequested(&self, atom: &AtomVmCreationRequested) -> Result<(), Status> {
145 forward_vm_creation_atom(atom);
146 Ok(())
147 }
148
149 fn atomVmExited(&self, atom: &AtomVmExited) -> Result<(), Status> {
150 forward_vm_exited_atom(atom);
151 Ok(())
152 }
153
154 fn debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>> {
155 check_debug_access()?;
156
157 let state = &mut *self.state.lock().unwrap();
158 let cids = state
159 .held_contexts
160 .iter()
161 .filter_map(|(_, inst)| Weak::upgrade(inst))
162 .map(|vm| VirtualMachineDebugInfo {
163 cid: vm.cid as i32,
164 temporaryDirectory: vm.get_temp_dir().to_string_lossy().to_string(),
165 requesterUid: vm.requester_uid as i32,
Charisee96113f32023-01-26 09:00:42 +0000166 requesterPid: vm.requester_debug_pid,
David Brazdilafc9a9e2023-01-12 16:08:10 +0000167 })
168 .collect();
169 Ok(cids)
170 }
Alice Wangc2fec932023-02-23 16:24:02 +0000171
172 fn requestCertificate(
173 &self,
174 csr: &[u8],
175 instance_img_fd: &ParcelFileDescriptor,
176 ) -> binder::Result<Vec<u8>> {
177 check_manage_access()?;
178 info!("Received csr. Getting certificate...");
Jiyong Park2227eaa2023-08-04 11:59:18 +0900179 request_certificate(csr, instance_img_fd)
180 .context("Failed to get certificate")
181 .with_log()
182 .or_service_specific_exception(-1)
Alice Wangc2fec932023-02-23 16:24:02 +0000183 }
Inseob Kim53d0b212023-07-20 16:58:37 +0900184
185 fn getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>> {
186 check_use_custom_virtual_machine()?;
187
188 // TODO(b/291191362): read VM DTBO to find assignable devices.
Inseob Kim55438b22023-08-09 20:16:01 +0900189 let mut devices = Vec::new();
190 let eh_path = "/sys/bus/platform/devices/16d00000.eh";
191 if Path::new(eh_path).exists() {
192 devices.push(AssignableDevice { kind: "eh".to_owned(), node: eh_path.to_owned() });
193 }
194 Ok(devices)
Inseob Kim53d0b212023-07-20 16:58:37 +0900195 }
Inseob Kim1ca0f652023-07-20 17:18:12 +0900196
Inseob Kimf36347b2023-08-03 12:52:48 +0900197 fn bindDevicesToVfioDriver(
198 &self,
199 devices: &[String],
200 dtbo: &ParcelFileDescriptor,
201 ) -> binder::Result<()> {
Inseob Kim1ca0f652023-07-20 17:18:12 +0900202 check_use_custom_virtual_machine()?;
203
Inseob Kimbdca0472023-07-28 19:20:56 +0900204 let vfio_service: Strong<dyn IVfioHandler> =
205 wait_for_interface(<BpVfioHandler as IVfioHandler>::get_descriptor())?;
Inseob Kimf36347b2023-08-03 12:52:48 +0900206
207 vfio_service.bindDevicesToVfioDriver(devices, dtbo)?;
208 Ok(())
Inseob Kim1ca0f652023-07-20 17:18:12 +0900209 }
210}
211
David Brazdilafc9a9e2023-01-12 16:08:10 +0000212#[derive(Debug, Default)]
213struct GlobalVmInstance {
214 /// The unique CID assigned to the VM for vsock communication.
215 cid: Cid,
216 /// UID of the client who requested this VM instance.
217 requester_uid: uid_t,
218 /// PID of the client who requested this VM instance.
219 requester_debug_pid: pid_t,
220}
221
222impl GlobalVmInstance {
223 fn get_temp_dir(&self) -> PathBuf {
224 let cid = self.cid;
225 format!("{TEMPORARY_DIRECTORY}/{cid}").into()
226 }
227}
228
229/// The mutable state of the VirtualizationServiceInternal. There should only be one instance
230/// of this struct.
231#[derive(Debug, Default)]
232struct GlobalState {
233 /// VM contexts currently allocated to running VMs. A CID is never recycled as long
234 /// as there is a strong reference held by a GlobalVmContext.
235 held_contexts: HashMap<Cid, Weak<GlobalVmInstance>>,
236}
237
238impl GlobalState {
239 /// Get the next available CID, or an error if we have run out. The last CID used is stored in
240 /// a system property so that restart of virtualizationservice doesn't reuse CID while the host
241 /// Android is up.
242 fn get_next_available_cid(&mut self) -> Result<Cid> {
243 // Start trying to find a CID from the last used CID + 1. This ensures
244 // that we do not eagerly recycle CIDs. It makes debugging easier but
245 // also means that retrying to allocate a CID, eg. because it is
246 // erroneously occupied by a process, will not recycle the same CID.
247 let last_cid_prop =
248 system_properties::read(SYSPROP_LAST_CID)?.and_then(|val| match val.parse::<Cid>() {
249 Ok(num) => {
250 if is_valid_guest_cid(num) {
251 Some(num)
252 } else {
253 error!("Invalid value '{}' of property '{}'", num, SYSPROP_LAST_CID);
254 None
255 }
256 }
257 Err(_) => {
258 error!("Invalid value '{}' of property '{}'", val, SYSPROP_LAST_CID);
259 None
260 }
261 });
262
263 let first_cid = if let Some(last_cid) = last_cid_prop {
264 if last_cid == GUEST_CID_MAX {
265 GUEST_CID_MIN
266 } else {
267 last_cid + 1
268 }
269 } else {
270 GUEST_CID_MIN
271 };
272
273 let cid = self
274 .find_available_cid(first_cid..=GUEST_CID_MAX)
275 .or_else(|| self.find_available_cid(GUEST_CID_MIN..first_cid))
276 .ok_or_else(|| anyhow!("Could not find an available CID."))?;
277
278 system_properties::write(SYSPROP_LAST_CID, &format!("{}", cid))?;
279 Ok(cid)
280 }
281
282 fn find_available_cid<I>(&self, mut range: I) -> Option<Cid>
283 where
284 I: Iterator<Item = Cid>,
285 {
286 range.find(|cid| !self.held_contexts.contains_key(cid))
287 }
288
289 fn allocate_vm_context(
290 &mut self,
291 requester_uid: uid_t,
292 requester_debug_pid: pid_t,
293 ) -> Result<Strong<dyn IGlobalVmContext>> {
294 // Garbage collect unused VM contexts.
295 self.held_contexts.retain(|_, instance| instance.strong_count() > 0);
296
297 let cid = self.get_next_available_cid()?;
298 let instance = Arc::new(GlobalVmInstance { cid, requester_uid, requester_debug_pid });
299 create_temporary_directory(&instance.get_temp_dir(), requester_uid)?;
300
301 self.held_contexts.insert(cid, Arc::downgrade(&instance));
302 let binder = GlobalVmContext { instance, ..Default::default() };
303 Ok(BnGlobalVmContext::new_binder(binder, BinderFeatures::default()))
304 }
305}
306
307fn create_temporary_directory(path: &PathBuf, requester_uid: uid_t) -> Result<()> {
308 if path.as_path().exists() {
309 remove_temporary_dir(path).unwrap_or_else(|e| {
310 warn!("Could not delete temporary directory {:?}: {}", path, e);
311 });
312 }
313 // Create a directory that is owned by client's UID but system's GID, and permissions 0700.
314 // If the chown() fails, this will leave behind an empty directory that will get removed
315 // at the next attempt, or if virtualizationservice is restarted.
316 create_dir(path).with_context(|| format!("Could not create temporary directory {:?}", path))?;
317 chown(path, Some(Uid::from_raw(requester_uid)), None)
318 .with_context(|| format!("Could not set ownership of temporary directory {:?}", path))?;
319 Ok(())
320}
321
322/// Removes a directory owned by a different user by first changing its owner back
323/// to VirtualizationService.
324pub fn remove_temporary_dir(path: &PathBuf) -> Result<()> {
Alice Wangd1b11a02023-04-18 12:30:20 +0000325 ensure!(path.as_path().is_dir(), "Path {:?} is not a directory", path);
David Brazdilafc9a9e2023-01-12 16:08:10 +0000326 chown(path, Some(Uid::current()), None)?;
327 set_permissions(path, Permissions::from_mode(0o700))?;
Alice Wangd1b11a02023-04-18 12:30:20 +0000328 remove_dir_all(path)?;
David Brazdilafc9a9e2023-01-12 16:08:10 +0000329 Ok(())
330}
331
332/// Implementation of the AIDL `IGlobalVmContext` interface.
333#[derive(Debug, Default)]
334struct GlobalVmContext {
335 /// Strong reference to the context's instance data structure.
336 instance: Arc<GlobalVmInstance>,
337 /// Keeps our service process running as long as this VM context exists.
338 #[allow(dead_code)]
339 lazy_service_guard: LazyServiceGuard,
340}
341
342impl Interface for GlobalVmContext {}
343
344impl IGlobalVmContext for GlobalVmContext {
345 fn getCid(&self) -> binder::Result<i32> {
346 Ok(self.instance.cid as i32)
347 }
348
349 fn getTemporaryDirectory(&self) -> binder::Result<String> {
350 Ok(self.instance.get_temp_dir().to_string_lossy().to_string())
351 }
352}
353
354fn handle_stream_connection_tombstoned() -> Result<()> {
355 // Should not listen for tombstones on a guest VM's port.
356 assert!(!is_valid_guest_cid(VM_TOMBSTONES_SERVICE_PORT as Cid));
357 let listener =
358 VsockListener::bind_with_cid_port(VMADDR_CID_HOST, VM_TOMBSTONES_SERVICE_PORT as Cid)?;
359 for incoming_stream in listener.incoming() {
360 let mut incoming_stream = match incoming_stream {
361 Err(e) => {
362 warn!("invalid incoming connection: {:?}", e);
363 continue;
364 }
365 Ok(s) => s,
366 };
367 std::thread::spawn(move || {
368 if let Err(e) = handle_tombstone(&mut incoming_stream) {
369 error!("Failed to write tombstone- {:?}", e);
370 }
371 });
372 }
373 Ok(())
374}
375
376fn handle_tombstone(stream: &mut VsockStream) -> Result<()> {
377 if let Ok(addr) = stream.peer_addr() {
378 info!("Vsock Stream connected to cid={} for tombstones", addr.cid());
379 }
380 let tb_connection =
381 TombstonedConnection::connect(std::process::id() as i32, DebuggerdDumpType::Tombstone)
382 .context("Failed to connect to tombstoned")?;
383 let mut text_output = tb_connection
384 .text_output
385 .as_ref()
386 .ok_or_else(|| anyhow!("Could not get file to write the tombstones on"))?;
387 let mut num_bytes_read = 0;
388 loop {
389 let mut chunk_recv = [0; CHUNK_RECV_MAX_LEN];
390 let n = stream
391 .read(&mut chunk_recv)
392 .context("Failed to read tombstone data from Vsock stream")?;
393 if n == 0 {
394 break;
395 }
396 num_bytes_read += n;
397 text_output.write_all(&chunk_recv[0..n]).context("Failed to write guests tombstones")?;
398 }
399 info!("Received {} bytes from guest & wrote to tombstone file", num_bytes_read);
400 tb_connection.notify_completion()?;
401 Ok(())
402}
403
404/// Checks whether the caller has a specific permission
405fn check_permission(perm: &str) -> binder::Result<()> {
406 let calling_pid = get_calling_pid();
407 let calling_uid = get_calling_uid();
408 // Root can do anything
409 if calling_uid == 0 {
410 return Ok(());
411 }
412 let perm_svc: Strong<dyn IPermissionController::IPermissionController> =
413 binder::get_interface("permission")?;
414 if perm_svc.checkPermission(perm, calling_pid, calling_uid as i32)? {
415 Ok(())
416 } else {
Jiyong Park2227eaa2023-08-04 11:59:18 +0900417 Err(anyhow!("does not have the {} permission", perm))
418 .or_binder_exception(ExceptionCode::SECURITY)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000419 }
420}
421
422/// Check whether the caller of the current Binder method is allowed to call debug methods.
423fn check_debug_access() -> binder::Result<()> {
424 check_permission("android.permission.DEBUG_VIRTUAL_MACHINE")
425}
426
427/// Check whether the caller of the current Binder method is allowed to manage VMs
428fn check_manage_access() -> binder::Result<()> {
429 check_permission("android.permission.MANAGE_VIRTUAL_MACHINE")
430}
Inseob Kim53d0b212023-07-20 16:58:37 +0900431
432/// Check whether the caller of the current Binder method is allowed to use custom VMs
433fn check_use_custom_virtual_machine() -> binder::Result<()> {
434 check_permission("android.permission.USE_CUSTOM_VIRTUAL_MACHINE")
435}