Allocate each VM an instance_id
Introduce a 64 bytes' long instance_id. When the VM is created, this is
requested from virtualization service, which randomly allocates it.
While it does that, it also logs the user & the package name.
The app persists this allocated instance_id in a file `instance_id` in
its directory for the vm instance (along with instance.img &
storage.img). When the VirtualMachine is run, this is an input into the
VM via DT.
This patch modifies Compos & vm binary to work with the instance_id.
flagging: instance_id allocation request is conditional to flag build
time flag llpvm_changes, no file `instance_id` is created if the flag is
off. `instanceId` is all 0s if the flag is off.
Bug: 291213394
Test: atest MicrodroidHostTest
Test: atest MicrodroidTests
Test: atest ComposHostTestCases
Test: Look for instance_id logged by VS
Change-Id: Ie8e25b9510e27362d4580c55c1bd557143ff7d0e
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index abaa74c..077a0ef 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -72,6 +72,7 @@
/// Start a new CompOS VM instance using the specified instance image file and parameters.
pub fn start(
service: &dyn IVirtualizationService,
+ instance_id: [u8; 64],
instance_image: File,
idsig: &Path,
idsig_manifest_apk: &Path,
@@ -121,6 +122,7 @@
name: parameters.name.clone(),
apk: Some(apk_fd),
idsig: Some(idsig_fd),
+ instanceId: instance_id,
instanceImage: Some(instance_fd),
encryptedStorageImage: None,
payload: Payload::ConfigPath(config_path),
diff --git a/compos/common/lib.rs b/compos/common/lib.rs
index 1f937c9..9d90717 100644
--- a/compos/common/lib.rs
+++ b/compos/common/lib.rs
@@ -39,6 +39,9 @@
/// tests.
pub const TEST_INSTANCE_DIR: &str = "test";
+/// The file that holds the instance_id of CompOS instance.
+pub const INSTANCE_ID_FILE: &str = "instance_id";
+
/// The file that holds the instance image for a CompOS instance.
pub const INSTANCE_IMAGE_FILE: &str = "instance.img";
diff --git a/compos/composd/src/instance_starter.rs b/compos/composd/src/instance_starter.rs
index 457520f..76001a4 100644
--- a/compos/composd/src/instance_starter.rs
+++ b/compos/composd/src/instance_starter.rs
@@ -20,13 +20,13 @@
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
IVirtualizationService::IVirtualizationService, PartitionType::PartitionType,
};
-use anyhow::{Context, Result};
+use anyhow::{anyhow, Context, Result};
use binder::{LazyServiceGuard, ParcelFileDescriptor, Strong};
use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
use compos_common::compos_client::{ComposClient, VmParameters};
use compos_common::{
COMPOS_DATA_ROOT, IDSIG_FILE, IDSIG_MANIFEST_APK_FILE, IDSIG_MANIFEST_EXT_APK_FILE,
- INSTANCE_IMAGE_FILE,
+ INSTANCE_ID_FILE, INSTANCE_IMAGE_FILE,
};
use log::info;
use std::fs;
@@ -66,6 +66,7 @@
pub struct InstanceStarter {
instance_name: String,
instance_root: PathBuf,
+ instance_id_file: PathBuf,
instance_image: PathBuf,
idsig: PathBuf,
idsig_manifest_apk: PathBuf,
@@ -77,6 +78,7 @@
pub fn new(instance_name: &str, vm_parameters: VmParameters) -> Self {
let instance_root = Path::new(COMPOS_DATA_ROOT).join(instance_name);
let instance_root_path = instance_root.as_path();
+ let instance_id_file = instance_root_path.join(INSTANCE_ID_FILE);
let instance_image = instance_root_path.join(INSTANCE_IMAGE_FILE);
let idsig = instance_root_path.join(IDSIG_FILE);
let idsig_manifest_apk = instance_root_path.join(IDSIG_MANIFEST_APK_FILE);
@@ -84,6 +86,7 @@
Self {
instance_name: instance_name.to_owned(),
instance_root,
+ instance_id_file,
instance_image,
idsig,
idsig_manifest_apk,
@@ -103,7 +106,10 @@
// Overwrite any existing instance - it's unlikely to be valid with the current set
// of APEXes, and finding out it isn't is much more expensive than creating a new one.
self.create_instance_image(virtualization_service)?;
-
+ // TODO(b/294177871): Ping VS to delete the old instance's secret.
+ if cfg!(llpvm_changes) {
+ self.allocate_instance_id(virtualization_service)?;
+ }
// Delete existing idsig files. Ignore error in case idsig doesn't exist.
let _ignored1 = fs::remove_file(&self.idsig);
let _ignored2 = fs::remove_file(&self.idsig_manifest_apk);
@@ -122,6 +128,14 @@
&self,
virtualization_service: &dyn IVirtualizationService,
) -> Result<CompOsInstance> {
+ let instance_id: [u8; 64] = if cfg!(llpvm_changes) {
+ fs::read(&self.instance_id_file)?
+ .try_into()
+ .map_err(|_| anyhow!("Failed to get instance_id"))?
+ } else {
+ [0u8; 64]
+ };
+
let instance_image = fs::OpenOptions::new()
.read(true)
.write(true)
@@ -129,6 +143,7 @@
.context("Failed to open instance image")?;
let vm_instance = ComposClient::start(
virtualization_service,
+ instance_id,
instance_image,
&self.idsig,
&self.idsig_manifest_apk,
@@ -164,4 +179,13 @@
.context("Writing instance image file")?;
Ok(())
}
+
+ fn allocate_instance_id(
+ &self,
+ virtualization_service: &dyn IVirtualizationService,
+ ) -> Result<()> {
+ let id = virtualization_service.allocateInstanceId().context("Allocating Instance Id")?;
+ fs::write(&self.instance_id_file, id)?;
+ Ok(())
+ }
}
diff --git a/compos/verify/verify.rs b/compos/verify/verify.rs
index 567083d..a3f18d5 100644
--- a/compos/verify/verify.rs
+++ b/compos/verify/verify.rs
@@ -18,7 +18,7 @@
//! public key. The tool is intended to be run by odsign during boot.
use android_logger::LogId;
-use anyhow::{bail, Context, Result};
+use anyhow::{anyhow, bail, Context, Result};
use binder::ProcessState;
use clap::{Parser, ValueEnum};
use compos_common::compos_client::{ComposClient, VmCpuTopology, VmParameters};
@@ -28,9 +28,10 @@
};
use compos_common::{
COMPOS_DATA_ROOT, CURRENT_INSTANCE_DIR, IDSIG_FILE, IDSIG_MANIFEST_APK_FILE,
- IDSIG_MANIFEST_EXT_APK_FILE, INSTANCE_IMAGE_FILE, TEST_INSTANCE_DIR,
+ IDSIG_MANIFEST_EXT_APK_FILE, INSTANCE_ID_FILE, INSTANCE_IMAGE_FILE, TEST_INSTANCE_DIR,
};
use log::error;
+use std::fs;
use std::fs::File;
use std::io::Read;
use std::panic;
@@ -90,11 +91,17 @@
bail!("{:?} is not a directory", instance_dir);
}
+ let instance_id_file = instance_dir.join(INSTANCE_ID_FILE);
let instance_image = instance_dir.join(INSTANCE_IMAGE_FILE);
let idsig = instance_dir.join(IDSIG_FILE);
let idsig_manifest_apk = instance_dir.join(IDSIG_MANIFEST_APK_FILE);
let idsig_manifest_ext_apk = instance_dir.join(IDSIG_MANIFEST_EXT_APK_FILE);
+ let instance_id: [u8; 64] = if cfg!(llpvm_changes) {
+ fs::read(instance_id_file)?.try_into().map_err(|_| anyhow!("Failed to get instance_id"))?
+ } else {
+ [0u8; 64]
+ };
let instance_image = File::open(instance_image).context("Failed to open instance image")?;
let info = artifacts_dir.join("compos.info");
@@ -110,6 +117,7 @@
let virtualization_service = virtmgr.connect()?;
let vm_instance = ComposClient::start(
&*virtualization_service,
+ instance_id,
instance_image,
&idsig,
&idsig_manifest_apk,
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachine.java b/java/framework/src/android/system/virtualmachine/VirtualMachine.java
index 6b03cfe..9a38acf 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachine.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachine.java
@@ -190,6 +190,9 @@
/** Name of the instance image file for a VM. (Not implemented) */
private static final String INSTANCE_IMAGE_FILE = "instance.img";
+ /** Name of the file for a VM containing Id. */
+ private static final String INSTANCE_ID_FILE = "instance_id";
+
/** Name of the idsig file for a VM */
private static final String IDSIG_FILE = "idsig";
@@ -227,6 +230,9 @@
/** File that backs the encrypted storage - Will be null if not enabled. */
@Nullable private final File mEncryptedStoreFilePath;
+ /** File that contains the Id. This is NULL iff FEATURE_LLPVM is disabled */
+ @Nullable private final File mInstanceIdPath;
+
/**
* Unmodifiable list of extra apks. Apks are specified by the vm config, and corresponding
* idsigs are to be generated.
@@ -376,6 +382,16 @@
File thisVmDir = getVmDir(context, mName);
mVmRootPath = thisVmDir;
mConfigFilePath = new File(thisVmDir, CONFIG_FILE);
+ try {
+ mInstanceIdPath =
+ (mVirtualizationService
+ .getBinder()
+ .isFeatureEnabled(IVirtualizationService.FEATURE_LLPVM_CHANGES))
+ ? new File(thisVmDir, INSTANCE_ID_FILE)
+ : null;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
mInstanceFilePath = new File(thisVmDir, INSTANCE_IMAGE_FILE);
mIdsigFilePath = new File(thisVmDir, IDSIG_FILE);
mExtraApks = setupExtraApks(context, config, thisVmDir);
@@ -414,6 +430,10 @@
VirtualMachineConfig config = VirtualMachineConfig.from(vmDescriptor.getConfigFd());
vm = new VirtualMachine(context, name, config, VirtualizationService.getInstance());
config.serialize(vm.mConfigFilePath);
+ if (vm.mInstanceIdPath != null) {
+ vm.importInstanceIdFrom(vmDescriptor.getInstanceIdFd());
+ }
+
try {
vm.mInstanceFilePath.createNewFile();
} catch (IOException e) {
@@ -475,6 +495,21 @@
IVirtualizationService service = vm.mVirtualizationService.getBinder();
+ if (vm.mInstanceIdPath != null) {
+ try (FileOutputStream stream = new FileOutputStream(vm.mInstanceIdPath)) {
+ byte[] id = service.allocateInstanceId();
+ stream.write(id);
+ } catch (FileNotFoundException e) {
+ throw new VirtualMachineException("instance_id file missing", e);
+ } catch (IOException e) {
+ throw new VirtualMachineException("failed to persist instance_id", e);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ } catch (ServiceSpecificException | IllegalArgumentException e) {
+ throw new VirtualMachineException("failed to create instance_id", e);
+ }
+ }
+
try {
service.initializeWritablePartition(
ParcelFileDescriptor.open(vm.mInstanceFilePath, MODE_READ_WRITE),
@@ -530,6 +565,9 @@
VirtualMachine vm =
new VirtualMachine(context, name, config, VirtualizationService.getInstance());
+ if (vm.mInstanceIdPath != null && !vm.mInstanceIdPath.exists()) {
+ throw new VirtualMachineException("instance_id file missing");
+ }
if (!vm.mInstanceFilePath.exists()) {
throw new VirtualMachineException("instance image missing");
}
@@ -547,6 +585,7 @@
// if a new VM is created with the same name (and files) that's unrelated.
mWasDeleted = true;
}
+ // TODO(b/294177871): Request deletion of VM secrets.
deleteVmDirectory(context, name);
}
@@ -814,6 +853,18 @@
VirtualMachineAppConfig appConfig =
vmConfig.toVsConfig(mContext.getPackageManager());
appConfig.name = mName;
+ if (mInstanceIdPath != null) {
+ appConfig.instanceId = Files.readAllBytes(mInstanceIdPath.toPath());
+ appConfig.instanceImage =
+ ParcelFileDescriptor.open(mInstanceFilePath, MODE_READ_WRITE);
+ } else {
+ // FEATURE_LLPVM_CHANGES is disabled, instance_id is not used.
+ appConfig.instanceId = new byte[64];
+ }
+ if (mEncryptedStoreFilePath != null) {
+ appConfig.encryptedStorageImage =
+ ParcelFileDescriptor.open(mEncryptedStoreFilePath, MODE_READ_WRITE);
+ }
if (!vmConfig.getExtraApks().isEmpty()) {
// Extra APKs were specified directly, rather than via config file.
@@ -835,7 +886,7 @@
}
try {
- createIdSigs(service, appConfig);
+ createIdSigsAndUpdateConfig(service, appConfig);
} catch (FileNotFoundException e) {
throw new VirtualMachineException("Failed to generate APK signature", e);
}
@@ -850,6 +901,8 @@
mVirtualMachine.registerCallback(new CallbackTranslator(service));
mContext.registerComponentCallbacks(mMemoryManagementCallbacks);
mVirtualMachine.start();
+ } catch (IOException e) {
+ throw new VirtualMachineException("failed to persist files", e);
} catch (IllegalStateException | ServiceSpecificException e) {
throw new VirtualMachineException(e);
} catch (RemoteException e) {
@@ -858,7 +911,8 @@
}
}
- private void createIdSigs(IVirtualizationService service, VirtualMachineAppConfig appConfig)
+ private void createIdSigsAndUpdateConfig(
+ IVirtualizationService service, VirtualMachineAppConfig appConfig)
throws RemoteException, FileNotFoundException {
// Fill the idsig file by hashing the apk
service.createOrUpdateIdsigFile(
@@ -872,11 +926,6 @@
// Re-open idsig files in read-only mode
appConfig.idsig = ParcelFileDescriptor.open(mIdsigFilePath, MODE_READ_ONLY);
- appConfig.instanceImage = ParcelFileDescriptor.open(mInstanceFilePath, MODE_READ_WRITE);
- if (mEncryptedStoreFilePath != null) {
- appConfig.encryptedStorageImage =
- ParcelFileDescriptor.open(mEncryptedStoreFilePath, MODE_READ_WRITE);
- }
List<ParcelFileDescriptor> extraIdsigs = new ArrayList<>();
for (ExtraApkSpec extraApk : mExtraApks) {
extraIdsigs.add(ParcelFileDescriptor.open(extraApk.idsig, MODE_READ_ONLY));
@@ -1249,6 +1298,7 @@
try {
return new VirtualMachineDescriptor(
ParcelFileDescriptor.open(mConfigFilePath, MODE_READ_ONLY),
+ ParcelFileDescriptor.open(mInstanceIdPath, MODE_READ_ONLY),
ParcelFileDescriptor.open(mInstanceFilePath, MODE_READ_ONLY),
mEncryptedStoreFilePath != null
? ParcelFileDescriptor.open(mEncryptedStoreFilePath, MODE_READ_ONLY)
@@ -1384,6 +1434,16 @@
return extraApks;
}
+ private void importInstanceIdFrom(@NonNull ParcelFileDescriptor instanceIdFd)
+ throws VirtualMachineException {
+ try (FileChannel idOutput = new FileOutputStream(mInstanceIdPath).getChannel();
+ FileChannel idInput = new AutoCloseInputStream(instanceIdFd).getChannel()) {
+ idOutput.transferFrom(idInput, /*position=*/ 0, idInput.size());
+ } catch (IOException e) {
+ throw new VirtualMachineException("failed to copy instance_id", e);
+ }
+ }
+
private void importInstanceFrom(@NonNull ParcelFileDescriptor instanceFd)
throws VirtualMachineException {
try (FileChannel instance = new FileOutputStream(mInstanceFilePath).getChannel();
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachineDescriptor.java b/java/framework/src/android/system/virtualmachine/VirtualMachineDescriptor.java
index 710925d..ee79256 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachineDescriptor.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachineDescriptor.java
@@ -40,6 +40,9 @@
public final class VirtualMachineDescriptor implements Parcelable, AutoCloseable {
private volatile boolean mClosed = false;
@NonNull private final ParcelFileDescriptor mConfigFd;
+ // File descriptor of the file containing the instance id - will be null iff
+ // FEATURE_LLPVM_CHANGES is disabled.
+ @Nullable private final ParcelFileDescriptor mInstanceIdFd;
@NonNull private final ParcelFileDescriptor mInstanceImgFd;
// File descriptor of the image backing the encrypted storage - Will be null if encrypted
// storage is not enabled. */
@@ -54,6 +57,7 @@
public void writeToParcel(@NonNull Parcel out, int flags) {
checkNotClosed();
out.writeParcelable(mConfigFd, flags);
+ out.writeParcelable(mInstanceIdFd, flags);
out.writeParcelable(mInstanceImgFd, flags);
out.writeParcelable(mEncryptedStoreFd, flags);
}
@@ -80,6 +84,15 @@
}
/**
+ * @return File descriptor of the file containing instance_id of the VM.
+ */
+ @Nullable
+ ParcelFileDescriptor getInstanceIdFd() {
+ checkNotClosed();
+ return mInstanceIdFd;
+ }
+
+ /**
* @return File descriptor of the instance.img of the VM.
*/
@NonNull
@@ -100,15 +113,18 @@
VirtualMachineDescriptor(
@NonNull ParcelFileDescriptor configFd,
+ @Nullable ParcelFileDescriptor instanceIdFd,
@NonNull ParcelFileDescriptor instanceImgFd,
@Nullable ParcelFileDescriptor encryptedStoreFd) {
mConfigFd = requireNonNull(configFd);
+ mInstanceIdFd = instanceIdFd;
mInstanceImgFd = requireNonNull(instanceImgFd);
mEncryptedStoreFd = encryptedStoreFd;
}
private VirtualMachineDescriptor(Parcel in) {
mConfigFd = requireNonNull(readParcelFileDescriptor(in));
+ mInstanceIdFd = readParcelFileDescriptor(in);
mInstanceImgFd = requireNonNull(readParcelFileDescriptor(in));
mEncryptedStoreFd = readParcelFileDescriptor(in);
}
@@ -127,6 +143,7 @@
mClosed = true;
// Let the compiler do the work: close everything, throw if any of them fail, skipping null.
try (mConfigFd;
+ mInstanceIdFd;
mInstanceImgFd;
mEncryptedStoreFd) {
} catch (IOException ignored) {
diff --git a/microdroid/README.md b/microdroid/README.md
index c82ef0b..6e7b20c 100644
--- a/microdroid/README.md
+++ b/microdroid/README.md
@@ -107,6 +107,7 @@
PATH_TO_YOUR_APP \
$TEST_ROOT/MyApp.apk.idsig \
$TEST_ROOT/instance.img \
+--instance-id-file $TEST_ROOT/instance_id \
--payload-binary-name MyMicrodroidPayload.so
```
diff --git a/service_vm/demo_apk/README.md b/service_vm/demo_apk/README.md
index 551d47b..8f67f6e 100644
--- a/service_vm/demo_apk/README.md
+++ b/service_vm/demo_apk/README.md
@@ -42,7 +42,9 @@
--config-path assets/config.json --debug full \
$(adb shell pm path com.android.virt.vm_attestation.demo | cut -c 9-) \
$TEST_ROOT/VmAttestationDemoApp.apk.idsig \
- $TEST_ROOT/instance.vm_attestation.debug.img --protected
+ $TEST_ROOT/instance.vm_attestation.debug.img \
+ --instance-id-file $TEST_ROOT/instance_id \
+ --protected
```
Please note that remote attestation is only available for protected VMs.
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
index 9e19b7d..2abf110 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
@@ -52,6 +52,7 @@
private static final int TEST_VM_ADB_PORT = 8000;
private static final String MICRODROID_SERIAL = "localhost:" + TEST_VM_ADB_PORT;
private static final String INSTANCE_IMG = "instance.img";
+ protected static final String VIRT_APEX = "/apex/com.android.virt/";
private static final long MICRODROID_ADB_CONNECT_TIMEOUT_MINUTES = 5;
protected static final long MICRODROID_COMMAND_TIMEOUT_MILLIS = 30000;
@@ -186,6 +187,12 @@
return ret;
}
+ public boolean isFeatureEnabled(String feature) throws Exception {
+ CommandRunner android = new CommandRunner(getDevice());
+ String result = android.run(VIRT_APEX + "bin/vm", "check-feature-enabled", feature);
+ return result.contains("enabled");
+ }
+
public List<String> getAssignableDevices() throws Exception {
return parseStringArrayFieldsFromVmInfo("Assignable devices: ");
}
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 4503cd3..9b95461 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -616,6 +616,7 @@
final String apkPath = getPathForPackage(PACKAGE_NAME);
final String idsigPath = TEST_ROOT + "idsig";
final String instanceImgPath = TEST_ROOT + "instance.img";
+ final String instanceIdPath = TEST_ROOT + "instance_id";
List<String> cmd =
new ArrayList<>(
Arrays.asList(
@@ -626,6 +627,11 @@
apkPath,
idsigPath,
instanceImgPath));
+ if (isFeatureEnabled("com.android.kvm.LLPVM_CHANGES")) {
+ cmd.add("--instance-id-file");
+ cmd.add(instanceIdPath);
+ }
+ ;
if (protectedVm) {
cmd.add("--protected");
}
@@ -886,7 +892,6 @@
final String apkPath = getPathForPackage(PACKAGE_NAME);
final String idSigPath = TEST_ROOT + "idsig";
android.run(VIRT_APEX + "bin/vm", "create-idsig", apkPath, idSigPath);
-
// Create the instance image for the VM
final String instanceImgPath = TEST_ROOT + "instance.img";
android.run(
@@ -896,17 +901,22 @@
instanceImgPath,
Integer.toString(10 * 1024 * 1024));
- final String ret =
- android.runForResult(
+ List<String> cmd =
+ new ArrayList<>(
+ Arrays.asList(
VIRT_APEX + "bin/vm",
"run-app",
"--payload-binary-name",
"./MicrodroidTestNativeLib.so",
apkPath,
idSigPath,
- instanceImgPath)
- .getStderr()
- .trim();
+ instanceImgPath));
+ if (isFeatureEnabled("com.android.kvm.LLPVM_CHANGES")) {
+ cmd.add("--instance-id-file");
+ cmd.add(TEST_ROOT + "instance_id");
+ }
+
+ final String ret = android.runForResult(String.join(" ", cmd)).getStderr().trim();
assertThat(ret).contains("Payload binary name must not specify a path");
}
diff --git a/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java b/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
index b84d7dc..26f5993 100644
--- a/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
+++ b/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
@@ -301,7 +301,7 @@
// Try to launch protected non-debuggable VM for a while and quit.
// Non-debuggable VM might not enable adb, so there's no ITestDevice instance of it.
- private CommandResult tryLaunchProtectedNonDebuggableVm() throws DeviceNotAvailableException {
+ private CommandResult tryLaunchProtectedNonDebuggableVm() throws Exception {
// Can't use MicrodroidBuilder because it expects adb connection
// but non-debuggable VM may not enable adb.
CommandRunner runner = new CommandRunner(mAndroidDevice);
@@ -314,7 +314,7 @@
String command =
String.join(
" ",
- "/apex/com.android.virt/bin/vm",
+ VIRT_APEX + "bin/vm",
"run-app",
"--log",
MICRODROID_LOG_PATH,
@@ -324,6 +324,9 @@
TEST_ROOT + "instance.img",
"--config-path",
MICRODROID_CONFIG_PATH);
+ if (isFeatureEnabled("com.android.kvm.LLPVM_CHANGES")) {
+ command = String.join(" ", command, "--instance-id-file", TEST_ROOT + "instance_id");
+ }
return mAndroidDevice.executeShellV2Command(
command, CONSOLE_OUTPUT_WAIT_MS, TimeUnit.MILLISECONDS, /* retryAttempts= */ 0);
}
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 6ba5cf4..7a1ce37 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -229,6 +229,11 @@
ret
}
+ /// Allocate a new instance_id to the VM
+ fn allocateInstanceId(&self) -> binder::Result<[u8; 64]> {
+ GLOBAL_SERVICE.allocateInstanceId()
+ }
+
/// Initialise an empty partition image of the given size to be used as a writable partition.
fn initializeWritablePartition(
&self,
@@ -398,9 +403,9 @@
vec![]
};
+ let instance_id;
let untrusted_props = if cfg!(llpvm_changes) {
- // TODO(b/291213394): Replace this with a per-VM instance Id.
- let instance_id = b"sixtyfourbyteslonghardcoded_indeed_sixtyfourbyteslonghardcoded_h";
+ instance_id = extract_instance_id(config);
vec![(cstr!("instance-id"), &instance_id[..])]
} else {
vec![]
@@ -1268,6 +1273,13 @@
}
}
+fn extract_instance_id(config: &VirtualMachineConfig) -> [u8; 64] {
+ match config {
+ VirtualMachineConfig::RawConfig(config) => config.instanceId,
+ VirtualMachineConfig::AppConfig(config) => config.instanceId,
+ }
+}
+
fn extract_gdb_port(config: &VirtualMachineConfig) -> Option<NonZeroU16> {
match config {
VirtualMachineConfig::RawConfig(config) => NonZeroU16::new(config.gdbPort as u16),
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 843873b..2cbc805 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -31,12 +31,14 @@
"libanyhow",
"libavflog",
"libbinder_rs",
+ "libhex",
"libhypervisor_props",
"liblazy_static",
"liblibc",
"liblog_rust",
"libnix",
"libopenssl",
+ "librand",
"librkpd_client",
"librustutils",
"libstatslog_virtualization_rust",
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
index f7bbf55..e11d8b8 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
@@ -41,6 +41,11 @@
in @nullable ParcelFileDescriptor osLogFd);
/**
+ * Allocate an instance_id to the (newly created) VM.
+ */
+ byte[64] allocateInstanceId();
+
+ /**
* Initialise an empty partition image of the given size to be used as a writable partition.
*
* The file must be open with both read and write permissions, and should be a new empty file.
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index 8302a2f..29232ff 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -23,6 +23,9 @@
/** Name of VM */
String name;
+ /** Id of the VM instance */
+ byte[64] instanceId;
+
/** Main APK */
ParcelFileDescriptor apk;
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index 6be2833..b2116c4 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -23,6 +23,9 @@
/** Name of VM */
String name;
+ /** Id of the VM instance */
+ byte[64] instanceId;
+
/** The kernel image, if any. */
@nullable ParcelFileDescriptor kernel;
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
index abfc45a..fc36190 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
@@ -91,4 +91,9 @@
/** Returns a read-only file descriptor of the VM DTBO file. */
ParcelFileDescriptor getDtboFile();
+
+ /**
+ * Allocate an instance_id to the (newly created) VM.
+ */
+ byte[64] allocateInstanceId();
}
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index c7ae5f0..79ff89a 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -35,6 +35,7 @@
use log::{error, info, warn};
use nix::unistd::{chown, Uid};
use openssl::x509::X509;
+use rand::Fill;
use rkpd_client::get_rkpd_attestation_key;
use rustutils::system_properties;
use serde::Deserialize;
@@ -378,6 +379,17 @@
let file = state.get_dtbo_file().or_service_specific_exception(-1)?;
Ok(ParcelFileDescriptor::new(file))
}
+
+ // TODO(b/294177871) Persist this Id, along with client uuid.
+ fn allocateInstanceId(&self) -> binder::Result<[u8; 64]> {
+ let mut id = [0u8; 64];
+ id.try_fill(&mut rand::thread_rng())
+ .context("Failed to allocate instance_id")
+ .or_service_specific_exception(-1)?;
+ let uid = get_calling_uid();
+ info!("Allocated a VM's instance_id: {:?}, for uid: {:?}", hex::encode(id), uid);
+ Ok(id)
+ }
}
// KEEP IN SYNC WITH assignable_devices.xsd
diff --git a/vm/src/main.rs b/vm/src/main.rs
index 355e193..063f992 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -22,6 +22,8 @@
CpuTopology::CpuTopology, IVirtualizationService::IVirtualizationService,
PartitionType::PartitionType, VirtualMachineAppConfig::DebugLevel::DebugLevel,
};
+#[cfg(not(llpvm_changes))]
+use anyhow::anyhow;
use anyhow::{Context, Error};
use binder::{ProcessState, Strong};
use clap::{Args, Parser};
@@ -162,6 +164,11 @@
/// Path to the instance image. Created if not exists.
instance: PathBuf,
+ /// Path to file containing instance_id. Required iff llpvm feature is enabled.
+ #[cfg(llpvm_changes)]
+ #[arg(long = "instance-id-file")]
+ instance_id: PathBuf,
+
/// Path to VM config JSON within APK (e.g. assets/vm_config.json)
#[arg(long)]
config_path: Option<String>,
@@ -192,6 +199,27 @@
fn extra_apks(&self) -> &[PathBuf] {
&[]
}
+
+ #[cfg(llpvm_changes)]
+ fn instance_id(&self) -> Result<PathBuf, Error> {
+ Ok(self.instance_id.clone())
+ }
+
+ #[cfg(not(llpvm_changes))]
+ fn instance_id(&self) -> Result<PathBuf, Error> {
+ Err(anyhow!("LLPVM feature is disabled, --instance_id flag not supported"))
+ }
+
+ #[cfg(llpvm_changes)]
+ fn set_instance_id(&mut self, instance_id_file: PathBuf) -> Result<(), Error> {
+ self.instance_id = instance_id_file;
+ Ok(())
+ }
+
+ #[cfg(not(llpvm_changes))]
+ fn set_instance_id(&mut self, _: PathBuf) -> Result<(), Error> {
+ Err(anyhow!("LLPVM feature is disabled, --instance_id flag not supported"))
+ }
}
#[derive(Args, Default)]
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 5a4a459..57b7641 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -35,6 +35,7 @@
use std::fs;
use std::fs::File;
use std::io;
+use std::io::{Read, Write};
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::path::{Path, PathBuf};
use vmclient::{ErrorCode, VmInstance};
@@ -84,6 +85,24 @@
)?;
}
+ let instance_id = if cfg!(llpvm_changes) {
+ let id_file = config.instance_id()?;
+ if id_file.exists() {
+ let mut id = [0u8; 64];
+ let mut instance_id_file = File::open(id_file)?;
+ instance_id_file.read_exact(&mut id)?;
+ id
+ } else {
+ let id = service.allocateInstanceId().context("Failed to allocate instance_id")?;
+ let mut instance_id_file = File::create(id_file)?;
+ instance_id_file.write_all(&id)?;
+ id
+ }
+ } else {
+ // if llpvm feature flag is disabled, instance_id is not used.
+ [0u8; 64]
+ };
+
let storage = if let Some(ref path) = config.microdroid.storage {
if !path.exists() {
command_create_partition(
@@ -153,6 +172,7 @@
idsig: idsig_fd.into(),
extraIdsigs: extra_idsig_fds,
instanceImage: open_parcel_file(&config.instance, true /* writable */)?.into(),
+ instanceId: instance_id,
encryptedStorageImage: storage,
payload,
debugLevel: config.debug.debug,
@@ -204,7 +224,7 @@
let instance_img = work_dir.join("instance.img");
println!("instance.img path: {}", instance_img.display());
- let app_config = RunAppConfig {
+ let mut app_config = RunAppConfig {
common: config.common,
debug: config.debug,
microdroid: config.microdroid,
@@ -214,6 +234,12 @@
payload_binary_name: Some("MicrodroidEmptyPayloadJniLib.so".to_owned()),
..Default::default()
};
+
+ if cfg!(llpvm_changes) {
+ app_config.set_instance_id(work_dir.join("instance_id"))?;
+ println!("instance_id file path: {}", app_config.instance_id()?.display());
+ }
+
command_run_app(app_config)
}