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 Virt Manager.
16 
17 use crate::config::VmConfig;
18 use crate::crosvm::VmInstance;
19 use crate::{Cid, FIRST_GUEST_CID};
20 use android_system_virtmanager::aidl::android::system::virtmanager::IVirtManager::IVirtManager;
21 use android_system_virtmanager::aidl::android::system::virtmanager::IVirtualMachine::{
22     BnVirtualMachine, IVirtualMachine,
23 };
24 use android_system_virtmanager::aidl::android::system::virtmanager::IVirtualMachineCallback::IVirtualMachineCallback;
25 use android_system_virtmanager::aidl::android::system::virtmanager::VirtualMachineDebugInfo::VirtualMachineDebugInfo;
26 use android_system_virtmanager::binder::{
27     self, BinderFeatures, Interface, ParcelFileDescriptor, StatusCode, Strong, ThreadState,
28 };
29 use log::{debug, error};
30 use std::fs::File;
31 use std::sync::{Arc, Mutex, Weak};
32 
33 pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtmanager";
34 
35 // TODO(qwandor): Use PermissionController once it is available to Rust.
36 /// Only processes running with one of these UIDs are allowed to call debug methods.
37 const DEBUG_ALLOWED_UIDS: [u32; 2] = [0, 2000];
38 
39 /// Implementation of `IVirtManager`, the entry point of the AIDL service.
40 #[derive(Debug, Default)]
41 pub struct VirtManager {
42     state: Mutex<State>,
43 }
44 
45 impl Interface for VirtManager {}
46 
47 impl IVirtManager for VirtManager {
48     /// Create and start a new VM with the given configuration, assigning it the next available CID.
49     ///
50     /// Returns a binder `IVirtualMachine` object referring to it, as a handle for the client.
51     fn startVm(
52         &self,
53         config_fd: &ParcelFileDescriptor,
54         log_fd: Option<&ParcelFileDescriptor>,
55     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
56         let state = &mut *self.state.lock().unwrap();
57         let log_fd = log_fd
58             .map(|fd| fd.as_ref().try_clone().map_err(|_| StatusCode::UNKNOWN_ERROR))
59             .transpose()?;
60         let requester_uid = ThreadState::get_calling_uid();
61         let requester_sid = ThreadState::with_calling_sid(|sid| {
62             if let Some(sid) = sid {
63                 match sid.to_str() {
64                     Ok(sid) => Ok(sid.to_owned()),
65                     Err(e) => {
66                         error!("SID was not valid UTF-8: {:?}", e);
67                         Err(StatusCode::BAD_VALUE)
68                     }
69                 }
70             } else {
71                 error!("Missing SID on startVm");
72                 Err(StatusCode::UNKNOWN_ERROR)
73             }
74         })?;
75         let requester_debug_pid = ThreadState::get_calling_pid();
76         let cid = state.allocate_cid()?;
77         let instance = start_vm(
78             config_fd.as_ref(),
79             cid,
80             log_fd,
81             requester_uid,
82             requester_sid,
83             requester_debug_pid,
84         )?;
85         state.add_vm(Arc::downgrade(&instance));
86         Ok(VirtualMachine::create(instance))
87     }
88 
89     /// Get a list of all currently running VMs. This method is only intended for debug purposes,
90     /// and as such is only permitted from the shell user.
91     fn debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>> {
92         if !debug_access_allowed() {
93             return Err(StatusCode::PERMISSION_DENIED.into());
94         }
95 
96         let state = &mut *self.state.lock().unwrap();
97         let vms = state.vms();
98         let cids = vms
99             .into_iter()
100             .map(|vm| VirtualMachineDebugInfo {
101                 cid: vm.cid as i32,
102                 requesterUid: vm.requester_uid as i32,
103                 requesterSid: vm.requester_sid.clone(),
104                 requesterPid: vm.requester_debug_pid,
105                 running: vm.running(),
106             })
107             .collect();
108         Ok(cids)
109     }
110 
111     /// Hold a strong reference to a VM in Virt Manager. This method is only intended for debug
112     /// purposes, and as such is only permitted from the shell user.
113     fn debugHoldVmRef(&self, vmref: &Strong<dyn IVirtualMachine>) -> binder::Result<()> {
114         if !debug_access_allowed() {
115             return Err(StatusCode::PERMISSION_DENIED.into());
116         }
117 
118         let state = &mut *self.state.lock().unwrap();
119         state.debug_hold_vm(vmref.clone());
120         Ok(())
121     }
122 
123     /// Drop reference to a VM that is being held by Virt Manager. Returns the reference if VM was
124     /// found and None otherwise. This method is only intended for debug purposes, and as such is
125     /// only permitted from the shell user.
126     fn debugDropVmRef(&self, cid: i32) -> binder::Result<Option<Strong<dyn IVirtualMachine>>> {
127         if !debug_access_allowed() {
128             return Err(StatusCode::PERMISSION_DENIED.into());
129         }
130 
131         let state = &mut *self.state.lock().unwrap();
132         Ok(state.debug_drop_vm(cid))
133     }
134 }
135 
136 /// Check whether the caller of the current Binder method is allowed to call debug methods.
137 fn debug_access_allowed() -> bool {
138     let uid = ThreadState::get_calling_uid();
139     log::trace!("Debug method call from UID {}.", uid);
140     DEBUG_ALLOWED_UIDS.contains(&uid)
141 }
142 
143 /// Implementation of the AIDL `IVirtualMachine` interface. Used as a handle to a VM.
144 #[derive(Debug)]
145 struct VirtualMachine {
146     instance: Arc<VmInstance>,
147 }
148 
149 impl VirtualMachine {
150     fn create(instance: Arc<VmInstance>) -> Strong<dyn IVirtualMachine> {
151         let binder = VirtualMachine { instance };
152         BnVirtualMachine::new_binder(binder, BinderFeatures::default())
153     }
154 }
155 
156 impl Interface for VirtualMachine {}
157 
158 impl IVirtualMachine for VirtualMachine {
159     fn getCid(&self) -> binder::Result<i32> {
160         Ok(self.instance.cid as i32)
161     }
162 
163     fn isRunning(&self) -> binder::Result<bool> {
164         Ok(self.instance.running())
165     }
166 
167     fn registerCallback(
168         &self,
169         callback: &Strong<dyn IVirtualMachineCallback>,
170     ) -> binder::Result<()> {
171         // TODO: Should this give an error if the VM is already dead?
172         self.instance.callbacks.add(callback.clone());
173         Ok(())
174     }
175 }
176 
177 impl Drop for VirtualMachine {
178     fn drop(&mut self) {
179         debug!("Dropping {:?}", self);
180         self.instance.kill();
181     }
182 }
183 
184 /// A set of Binders to be called back in response to various events on the VM, such as when it
185 /// dies.
186 #[derive(Debug, Default)]
187 pub struct VirtualMachineCallbacks(Mutex<Vec<Strong<dyn IVirtualMachineCallback>>>);
188 
189 impl VirtualMachineCallbacks {
190     /// Call all registered callbacks to say that the VM has died.
191     pub fn callback_on_died(&self, cid: Cid) {
192         let callbacks = &*self.0.lock().unwrap();
193         for callback in callbacks {
194             if let Err(e) = callback.onDied(cid as i32) {
195                 error!("Error calling callback: {}", e);
196             }
197         }
198     }
199 
200     /// Add a new callback to the set.
201     fn add(&self, callback: Strong<dyn IVirtualMachineCallback>) {
202         self.0.lock().unwrap().push(callback);
203     }
204 }
205 
206 /// The mutable state of the Virt Manager. There should only be one instance of this struct.
207 #[derive(Debug)]
208 struct State {
209     /// The next available unused CID.
210     next_cid: Cid,
211 
212     /// The VMs which have been started. When VMs are started a weak reference is added to this list
213     /// while a strong reference is returned to the caller over Binder. Once all copies of the
214     /// Binder client are dropped the weak reference here will become invalid, and will be removed
215     /// from the list opportunistically the next time `add_vm` is called.
216     vms: Vec<Weak<VmInstance>>,
217 
218     /// Vector of strong VM references held on behalf of users that cannot hold them themselves.
219     /// This is only used for debugging purposes.
220     debug_held_vms: Vec<Strong<dyn IVirtualMachine>>,
221 }
222 
223 impl State {
224     /// Get a list of VMs which still have Binder references to them.
225     fn vms(&self) -> Vec<Arc<VmInstance>> {
226         // Attempt to upgrade the weak pointers to strong pointers.
227         self.vms.iter().filter_map(Weak::upgrade).collect()
228     }
229 
230     /// Add a new VM to the list.
231     fn add_vm(&mut self, vm: Weak<VmInstance>) {
232         // Garbage collect any entries from the stored list which no longer exist.
233         self.vms.retain(|vm| vm.strong_count() > 0);
234 
235         // Actually add the new VM.
236         self.vms.push(vm);
237     }
238 
239     /// Store a strong VM reference.
240     fn debug_hold_vm(&mut self, vm: Strong<dyn IVirtualMachine>) {
241         self.debug_held_vms.push(vm);
242     }
243 
244     /// Retrieve and remove a strong VM reference.
245     fn debug_drop_vm(&mut self, cid: i32) -> Option<Strong<dyn IVirtualMachine>> {
246         let pos = self.debug_held_vms.iter().position(|vm| vm.getCid() == Ok(cid))?;
247         Some(self.debug_held_vms.swap_remove(pos))
248     }
249 
250     /// Get the next available CID, or an error if we have run out.
251     fn allocate_cid(&mut self) -> binder::Result<Cid> {
252         // TODO(qwandor): keep track of which CIDs are currently in use so that we can reuse them.
253         let cid = self.next_cid;
254         self.next_cid = self.next_cid.checked_add(1).ok_or(StatusCode::UNKNOWN_ERROR)?;
255         Ok(cid)
256     }
257 }
258 
259 impl Default for State {
260     fn default() -> Self {
261         State { next_cid: FIRST_GUEST_CID, vms: vec![], debug_held_vms: vec![] }
262     }
263 }
264 
265 /// Start a new VM instance from the given VM config file. This assumes the VM is not already
266 /// running.
267 fn start_vm(
268     config_file: &File,
269     cid: Cid,
270     log_fd: Option<File>,
271     requester_uid: u32,
272     requester_sid: String,
273     requester_debug_pid: i32,
274 ) -> binder::Result<Arc<VmInstance>> {
275     let config = VmConfig::load(config_file).map_err(|e| {
276         error!("Failed to load VM config from {:?}: {:?}", config_file, e);
277         StatusCode::BAD_VALUE
278     })?;
279     Ok(VmInstance::start(&config, cid, log_fd, requester_uid, requester_sid, requester_debug_pid)
280         .map_err(|e| {
281         error!("Failed to start VM from {:?}: {:?}", config_file, e);
282         StatusCode::UNKNOWN_ERROR
283     })?)
284 }
285