virtmgr: Delegate debugVmList to virtualizationservice
The shell user has permission to list all VMs in the system (called from 'vm list'). This stopped working after virtmgr was introduced, as it only has visibility of its own VMs. Add a new method on VirtuliazationService_Internal to retrieve this information from the global VS, which does keep a list of the allocated VM contexts. Extend VS's info with the requestor's UID and PID to match the original functionality. The one piece of information missing is the VM's running state. Bug: 245727626 Bug: 264826962 Test: adb shell /apex/com.android.virt/vm list Test: vm/vm_shell.sh start-microdroid --auto-connect Test: atest MicrodroidHostTestCases Change-Id: I4cf5d1a9792aca995b63a13c2ee47c6033d9a5d6
This commit is contained in:
parent
31c22e6dea
commit
d4f51a53af
|
@ -652,6 +652,8 @@ public class MicrodroidHostTests extends MicrodroidHostTestCaseBase {
|
|||
@Test
|
||||
@CddTest(requirements = {"9.17/C-1-1", "9.17/C-1-2", "9.17/C/1-3"})
|
||||
public void testMicrodroidBoots() throws Exception {
|
||||
CommandRunner android = new CommandRunner(getDevice());
|
||||
|
||||
final String configPath = "assets/vm_config.json"; // path inside the APK
|
||||
mMicrodroidDevice =
|
||||
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
|
||||
|
@ -662,6 +664,9 @@ public class MicrodroidHostTests extends MicrodroidHostTestCaseBase {
|
|||
mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
|
||||
CommandRunner microdroid = new CommandRunner(mMicrodroidDevice);
|
||||
|
||||
String vmList = android.run("/apex/com.android.virt/bin/vm list");
|
||||
assertThat(vmList).contains("requesterUid: " + android.run("id -u"));
|
||||
|
||||
// Test writing to /data partition
|
||||
microdroid.run("echo MicrodroidTest > /data/local/tmp/test.txt");
|
||||
assertThat(microdroid.run("cat /data/local/tmp/test.txt")).isEqualTo("MicrodroidTest");
|
||||
|
@ -680,7 +685,6 @@ public class MicrodroidHostTests extends MicrodroidHostTestCaseBase {
|
|||
assertThat(abis).hasLength(1);
|
||||
|
||||
// Check that no denials have happened so far
|
||||
CommandRunner android = new CommandRunner(getDevice());
|
||||
assertThat(android.tryRun("egrep", "'avc:[[:space:]]{1,2}denied'", LOG_PATH)).isNull();
|
||||
assertThat(android.tryRun("egrep", "'avc:[[:space:]]{1,2}denied'", CONSOLE_PATH)).isNull();
|
||||
|
||||
|
|
|
@ -36,7 +36,10 @@ aidl_interface {
|
|||
aidl_interface {
|
||||
name: "android.system.virtualizationservice_internal",
|
||||
srcs: ["android/system/virtualizationservice_internal/**/*.aidl"],
|
||||
imports: ["android.system.virtualizationcommon"],
|
||||
imports: [
|
||||
"android.system.virtualizationcommon",
|
||||
"android.system.virtualizationservice",
|
||||
],
|
||||
unstable: true,
|
||||
backend: {
|
||||
java: {
|
||||
|
|
|
@ -33,7 +33,4 @@ parcelable VirtualMachineDebugInfo {
|
|||
* the PID may have been reused for a different process, so this should not be trusted.
|
||||
*/
|
||||
int requesterPid;
|
||||
|
||||
/** The current lifecycle state of the VM. */
|
||||
VirtualMachineState state = VirtualMachineState.NOT_STARTED;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package android.system.virtualizationservice_internal;
|
||||
|
||||
import android.system.virtualizationservice.VirtualMachineDebugInfo;
|
||||
import android.system.virtualizationservice_internal.AtomVmBooted;
|
||||
import android.system.virtualizationservice_internal.AtomVmCreationRequested;
|
||||
import android.system.virtualizationservice_internal.AtomVmExited;
|
||||
|
@ -35,7 +36,7 @@ interface IVirtualizationServiceInternal {
|
|||
* The resources will not be recycled as long as there is a strong reference
|
||||
* to the returned object.
|
||||
*/
|
||||
IGlobalVmContext allocateGlobalVmContext();
|
||||
IGlobalVmContext allocateGlobalVmContext(int requesterDebugPid);
|
||||
|
||||
/** Forwards a VmBooted atom to statsd. */
|
||||
void atomVmBooted(in AtomVmBooted atom);
|
||||
|
@ -45,4 +46,7 @@ interface IVirtualizationServiceInternal {
|
|||
|
||||
/** Forwards a VmExited atom to statsd. */
|
||||
void atomVmExited(in AtomVmExited atom);
|
||||
|
||||
/** Get a list of all currently running VMs. */
|
||||
VirtualMachineDebugInfo[] debugListVms();
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ use std::io::{Error, ErrorKind, Read, Write};
|
|||
use std::num::NonZeroU32;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::os::unix::io::{FromRawFd, IntoRawFd};
|
||||
use std::os::unix::raw::uid_t;
|
||||
use std::os::unix::raw::{pid_t, uid_t};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use tombstoned_client::{DebuggerdDumpType, TombstonedConnection};
|
||||
|
@ -189,10 +189,14 @@ impl IVirtualizationServiceInternal for VirtualizationServiceInternal {
|
|||
}
|
||||
}
|
||||
|
||||
fn allocateGlobalVmContext(&self) -> binder::Result<Strong<dyn IGlobalVmContext>> {
|
||||
fn allocateGlobalVmContext(
|
||||
&self,
|
||||
requester_debug_pid: i32,
|
||||
) -> binder::Result<Strong<dyn IGlobalVmContext>> {
|
||||
let requester_uid = get_calling_uid();
|
||||
let requester_debug_pid = requester_debug_pid as pid_t;
|
||||
let state = &mut *self.state.lock().unwrap();
|
||||
state.allocate_vm_context(requester_uid).map_err(|e| {
|
||||
state.allocate_vm_context(requester_uid, requester_debug_pid).map_err(|e| {
|
||||
Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some(e.to_string()))
|
||||
})
|
||||
}
|
||||
|
@ -211,12 +215,32 @@ impl IVirtualizationServiceInternal for VirtualizationServiceInternal {
|
|||
forward_vm_exited_atom(atom);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>> {
|
||||
let state = &mut *self.state.lock().unwrap();
|
||||
let cids = state
|
||||
.held_contexts
|
||||
.iter()
|
||||
.filter_map(|(_, inst)| Weak::upgrade(inst))
|
||||
.map(|vm| VirtualMachineDebugInfo {
|
||||
cid: vm.cid as i32,
|
||||
temporaryDirectory: vm.get_temp_dir().to_string_lossy().to_string(),
|
||||
requesterUid: vm.requester_uid as i32,
|
||||
requesterPid: vm.requester_debug_pid as i32,
|
||||
})
|
||||
.collect();
|
||||
Ok(cids)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct GlobalVmInstance {
|
||||
/// The unique CID assigned to the VM for vsock communication.
|
||||
cid: Cid,
|
||||
/// UID of the client who requested this VM instance.
|
||||
requester_uid: uid_t,
|
||||
/// PID of the client who requested this VM instance.
|
||||
requester_debug_pid: pid_t,
|
||||
}
|
||||
|
||||
impl GlobalVmInstance {
|
||||
|
@ -289,12 +313,13 @@ impl GlobalState {
|
|||
fn allocate_vm_context(
|
||||
&mut self,
|
||||
requester_uid: uid_t,
|
||||
requester_debug_pid: pid_t,
|
||||
) -> Result<Strong<dyn IGlobalVmContext>> {
|
||||
// Garbage collect unused VM contexts.
|
||||
self.held_contexts.retain(|_, instance| instance.strong_count() > 0);
|
||||
|
||||
let cid = self.get_next_available_cid()?;
|
||||
let instance = Arc::new(GlobalVmInstance { cid });
|
||||
let instance = Arc::new(GlobalVmInstance { cid, requester_uid, requester_debug_pid });
|
||||
create_temporary_directory(&instance.get_temp_dir(), requester_uid)?;
|
||||
|
||||
self.held_contexts.insert(cid, Arc::downgrade(&instance));
|
||||
|
@ -475,20 +500,7 @@ impl IVirtualizationService for VirtualizationService {
|
|||
/// and as such is only permitted from the shell user.
|
||||
fn debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>> {
|
||||
check_debug_access()?;
|
||||
|
||||
let state = &mut *self.state.lock().unwrap();
|
||||
let vms = state.vms();
|
||||
let cids = vms
|
||||
.into_iter()
|
||||
.map(|vm| VirtualMachineDebugInfo {
|
||||
cid: vm.cid as i32,
|
||||
temporaryDirectory: vm.temporary_directory.to_string_lossy().to_string(),
|
||||
requesterUid: vm.requester_uid as i32,
|
||||
requesterPid: vm.requester_debug_pid,
|
||||
state: get_state(&vm),
|
||||
})
|
||||
.collect();
|
||||
Ok(cids)
|
||||
GLOBAL_SERVICE.debugListVms()
|
||||
}
|
||||
|
||||
/// Hold a strong reference to a VM in VirtualizationService. This method is only intended for
|
||||
|
@ -570,13 +582,13 @@ impl VirtualizationService {
|
|||
VirtualizationService::default()
|
||||
}
|
||||
|
||||
fn create_vm_context(&self) -> Result<(VmContext, Cid, PathBuf)> {
|
||||
fn create_vm_context(&self, requester_debug_pid: pid_t) -> Result<(VmContext, Cid, PathBuf)> {
|
||||
const NUM_ATTEMPTS: usize = 5;
|
||||
|
||||
for _ in 0..NUM_ATTEMPTS {
|
||||
let global_context = GLOBAL_SERVICE.allocateGlobalVmContext()?;
|
||||
let cid = global_context.getCid()? as Cid;
|
||||
let temp_dir: PathBuf = global_context.getTemporaryDirectory()?.into();
|
||||
let vm_context = GLOBAL_SERVICE.allocateGlobalVmContext(requester_debug_pid as i32)?;
|
||||
let cid = vm_context.getCid()? as Cid;
|
||||
let temp_dir: PathBuf = vm_context.getTemporaryDirectory()?.into();
|
||||
let service = VirtualMachineService::new_binder(self.state.clone(), cid).as_binder();
|
||||
|
||||
// Start VM service listening for connections from the new CID on port=CID.
|
||||
|
@ -584,7 +596,7 @@ impl VirtualizationService {
|
|||
match RpcServer::new_vsock(service, cid, port) {
|
||||
Ok(vm_server) => {
|
||||
vm_server.start();
|
||||
return Ok((VmContext::new(global_context, vm_server), cid, temp_dir));
|
||||
return Ok((VmContext::new(vm_context, vm_server), cid, temp_dir));
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Could not start RpcServer on port {}: {}", port, err);
|
||||
|
@ -617,19 +629,21 @@ impl VirtualizationService {
|
|||
check_use_custom_virtual_machine()?;
|
||||
}
|
||||
|
||||
let (vm_context, cid, temporary_directory) = self.create_vm_context().map_err(|e| {
|
||||
error!("Failed to create VmContext: {:?}", e);
|
||||
Status::new_service_specific_error_str(
|
||||
-1,
|
||||
Some(format!("Failed to create VmContext: {:?}", e)),
|
||||
)
|
||||
})?;
|
||||
let requester_uid = get_calling_uid();
|
||||
let requester_debug_pid = get_calling_pid();
|
||||
|
||||
let (vm_context, cid, temporary_directory) =
|
||||
self.create_vm_context(requester_debug_pid).map_err(|e| {
|
||||
error!("Failed to create VmContext: {:?}", e);
|
||||
Status::new_service_specific_error_str(
|
||||
-1,
|
||||
Some(format!("Failed to create VmContext: {:?}", e)),
|
||||
)
|
||||
})?;
|
||||
|
||||
let state = &mut *self.state.lock().unwrap();
|
||||
let console_fd = console_fd.map(clone_file).transpose()?;
|
||||
let log_fd = log_fd.map(clone_file).transpose()?;
|
||||
let requester_uid = get_calling_uid();
|
||||
let requester_debug_pid = get_calling_pid();
|
||||
|
||||
// Counter to generate unique IDs for temporary image files.
|
||||
let mut next_temporary_image_id = 0;
|
||||
|
|
Loading…
Reference in New Issue