Merge changes Ia10f2b6e,If05b871b
* changes:
Serialise number of CPUs.
Don't fall back to unprotected mode if protected is not supported.
diff --git a/compos/Android.bp b/compos/Android.bp
index c54348a..0bcbcdd 100644
--- a/compos/Android.bp
+++ b/compos/Android.bp
@@ -28,6 +28,7 @@
"libprotobuf",
"libregex",
"libring",
+ "librustutils",
"libscopeguard",
],
prefer_rlib: true,
diff --git a/compos/aidl/com/android/compos/ICompOsService.aidl b/compos/aidl/com/android/compos/ICompOsService.aidl
index cead5d0..18e163e 100644
--- a/compos/aidl/com/android/compos/ICompOsService.aidl
+++ b/compos/aidl/com/android/compos/ICompOsService.aidl
@@ -21,6 +21,17 @@
/** {@hide} */
interface ICompOsService {
/**
+ * What type of compilation to perform.
+ */
+ @Backing(type="int")
+ enum CompilationMode {
+ /** Compile artifacts required by the current set of APEXes for use on reboot. */
+ NORMAL_COMPILE = 0,
+ /** Compile a full set of artifacts for test purposes. */
+ TEST_COMPILE = 1,
+ }
+
+ /**
* Initializes the service with the supplied encrypted private key blob. The key cannot be
* changed once initialized, so once initiailzed, a repeated call will fail with
* EX_ILLEGAL_STATE.
@@ -37,6 +48,7 @@
* through systemDirFd over AuthFS), and *CLASSPATH derived in the VM, to generate the same
* odrefresh output artifacts to the output directory (through outputDirFd).
*
+ * @param compilationMode The type of compilation to be performed
* @param systemDirFd An fd referring to /system
* @param outputDirFd An fd referring to the output directory, ART_APEX_DATA
* @param stagingDirFd An fd referring to the staging directory, e.g. ART_APEX_DATA/staging
@@ -46,8 +58,9 @@
* @param systemServerCompilerFilter The compiler filter used to compile system server
* @return odrefresh exit code
*/
- byte odrefresh(int systemDirFd, int outputDirFd, int stagingDirFd, String targetDirName,
- String zygoteArch, String systemServerCompilerFilter);
+ byte odrefresh(CompilationMode compilation_mode, int systemDirFd, int outputDirFd,
+ int stagingDirFd, String targetDirName, String zygoteArch,
+ String systemServerCompilerFilter);
/**
* Generate a new public/private key pair suitable for signing CompOs output files.
diff --git a/compos/composd/src/odrefresh_task.rs b/compos/composd/src/odrefresh_task.rs
index 330f0ab..47ff590 100644
--- a/compos/composd/src/odrefresh_task.rs
+++ b/compos/composd/src/odrefresh_task.rs
@@ -23,7 +23,9 @@
};
use android_system_composd::binder::{Interface, Result as BinderResult, Strong};
use anyhow::{Context, Result};
-use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::{
+ CompilationMode::CompilationMode, ICompOsService,
+};
use compos_common::odrefresh::ExitCode;
use log::{error, warn};
use rustutils::system_properties;
@@ -68,6 +70,7 @@
pub fn start(
comp_os: Arc<CompOsInstance>,
+ compilation_mode: CompilationMode,
target_dir_name: String,
callback: &Strong<dyn ICompilationTaskCallback>,
) -> Result<OdrefreshTask> {
@@ -75,14 +78,19 @@
let task = RunningTask { comp_os, callback: callback.clone() };
let task = OdrefreshTask { running_task: Arc::new(Mutex::new(Some(task))) };
- task.clone().start_thread(service, target_dir_name);
+ task.clone().start_thread(service, compilation_mode, target_dir_name);
Ok(task)
}
- fn start_thread(self, service: Strong<dyn ICompOsService>, target_dir_name: String) {
+ fn start_thread(
+ self,
+ service: Strong<dyn ICompOsService>,
+ compilation_mode: CompilationMode,
+ target_dir_name: String,
+ ) {
thread::spawn(move || {
- let exit_code = run_in_vm(service, &target_dir_name);
+ let exit_code = run_in_vm(service, compilation_mode, &target_dir_name);
let task = self.take();
// We don't do the callback if cancel has already happened.
@@ -106,7 +114,11 @@
}
}
-fn run_in_vm(service: Strong<dyn ICompOsService>, target_dir_name: &str) -> Result<ExitCode> {
+fn run_in_vm(
+ service: Strong<dyn ICompOsService>,
+ compilation_mode: CompilationMode,
+ target_dir_name: &str,
+) -> Result<ExitCode> {
let output_root = Path::new(ART_APEX_DATA);
// We need to remove the target directory because odrefresh running in compos will create it
@@ -134,6 +146,7 @@
let system_server_compiler_filter =
system_properties::read("dalvik.vm.systemservercompilerfilter").unwrap_or_default();
let exit_code = service.odrefresh(
+ compilation_mode,
system_dir.as_raw_fd(),
output_dir.as_raw_fd(),
staging_dir.as_raw_fd(),
diff --git a/compos/composd/src/service.rs b/compos/composd/src/service.rs
index 6cdcd85..f4121e7 100644
--- a/compos/composd/src/service.rs
+++ b/compos/composd/src/service.rs
@@ -28,6 +28,7 @@
self, BinderFeatures, ExceptionCode, Interface, Status, Strong, ThreadState,
};
use anyhow::{Context, Result};
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::CompilationMode::CompilationMode;
use compos_common::binder::to_binder_result;
use rustutils::{users::AID_ROOT, users::AID_SYSTEM};
use std::sync::Arc;
@@ -72,7 +73,12 @@
let comp_os = self.instance_manager.start_pending_instance().context("Starting CompOS")?;
let target_dir_name = "compos-pending".to_owned();
- let task = OdrefreshTask::start(comp_os, target_dir_name, callback)?;
+ let task = OdrefreshTask::start(
+ comp_os,
+ CompilationMode::NORMAL_COMPILE,
+ target_dir_name,
+ callback,
+ )?;
Ok(BnCompilationTask::new_binder(task, BinderFeatures::default()))
}
@@ -84,7 +90,12 @@
let comp_os = self.instance_manager.start_test_instance().context("Starting CompOS")?;
let target_dir_name = "test-artifacts".to_owned();
- let task = OdrefreshTask::start(comp_os, target_dir_name, callback)?;
+ let task = OdrefreshTask::start(
+ comp_os,
+ CompilationMode::TEST_COMPILE,
+ target_dir_name,
+ callback,
+ )?;
Ok(BnCompilationTask::new_binder(task, BinderFeatures::default()))
}
diff --git a/compos/src/compilation.rs b/compos/src/compilation.rs
index e8f55f8..7e3834a 100644
--- a/compos/src/compilation.rs
+++ b/compos/src/compilation.rs
@@ -18,6 +18,7 @@
use log::{debug, info, warn};
use minijail::{self, Minijail};
use regex::Regex;
+use rustutils::system_properties;
use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
@@ -35,11 +36,13 @@
IAuthFsService::IAuthFsService,
};
use authfs_aidl_interface::binder::Strong;
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::CompilationMode::CompilationMode;
use compos_common::odrefresh::ExitCode;
const FD_SERVER_PORT: i32 = 3264; // TODO: support dynamic port
pub struct OdrefreshContext<'a> {
+ compilation_mode: CompilationMode,
system_dir_fd: i32,
output_dir_fd: i32,
staging_dir_fd: i32,
@@ -50,6 +53,7 @@
impl<'a> OdrefreshContext<'a> {
pub fn new(
+ compilation_mode: CompilationMode,
system_dir_fd: i32,
output_dir_fd: i32,
staging_dir_fd: i32,
@@ -57,6 +61,13 @@
zygote_arch: &'a str,
system_server_compiler_filter: &'a str,
) -> Result<Self> {
+ if compilation_mode != CompilationMode::NORMAL_COMPILE {
+ let debuggable = system_properties::read_bool("ro.boot.microdroid.debuggable", false)?;
+ if !debuggable {
+ bail!("Requested compilation mode only available in debuggable VMs");
+ }
+ }
+
if system_dir_fd < 0 || output_dir_fd < 0 || staging_dir_fd < 0 {
bail!("The remote FDs are expected to be non-negative");
}
@@ -75,6 +86,7 @@
// CompOS.
Ok(Self {
+ compilation_mode,
system_dir_fd,
output_dir_fd,
staging_dir_fd,
@@ -143,18 +155,21 @@
));
}
- args.push("--compile".to_string());
+ let compile_flag = match context.compilation_mode {
+ CompilationMode::NORMAL_COMPILE => "--compile",
+ CompilationMode::TEST_COMPILE => "--force-compile",
+ other => bail!("Unknown compilation mode {:?}", other),
+ };
+ args.push(compile_flag.to_string());
debug!("Running odrefresh with args: {:?}", &args);
let jail = spawn_jailed_task(odrefresh_path, &args, &odrefresh_vars.into_env())
.context("Spawn odrefresh")?;
let exit_code = match jail.wait() {
- Ok(_) => Result::<u8>::Ok(0),
- Err(minijail::Error::ReturnCode(exit_code)) => Ok(exit_code),
- Err(e) => {
- bail!("Unexpected minijail error: {}", e)
- }
- }?;
+ Ok(_) => 0,
+ Err(minijail::Error::ReturnCode(exit_code)) => exit_code,
+ Err(e) => bail!("Unexpected minijail error: {}", e),
+ };
let exit_code = ExitCode::from_i32(exit_code.into())?;
info!("odrefresh exited with {:?}", exit_code);
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index 422f271..9d754a7 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -31,7 +31,7 @@
use authfs_aidl_interface::aidl::com::android::virt::fs::IAuthFsService::IAuthFsService;
use compos_aidl_interface::aidl::com::android::compos::{
CompOsKeyData::CompOsKeyData,
- ICompOsService::{BnCompOsService, ICompOsService},
+ ICompOsService::{BnCompOsService, CompilationMode::CompilationMode, ICompOsService},
};
use compos_aidl_interface::binder::{
BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Strong,
@@ -82,6 +82,7 @@
fn odrefresh(
&self,
+ compilation_mode: CompilationMode,
system_dir_fd: i32,
output_dir_fd: i32,
staging_dir_fd: i32,
@@ -90,6 +91,7 @@
system_server_compiler_filter: &str,
) -> BinderResult<i8> {
let context = to_binder_result(OdrefreshContext::new(
+ compilation_mode,
system_dir_fd,
output_dir_fd,
staging_dir_fd,
diff --git a/microdroid_manager/src/instance.rs b/microdroid_manager/src/instance.rs
index 5a77198..f3bbf16 100644
--- a/microdroid_manager/src/instance.rs
+++ b/microdroid_manager/src/instance.rs
@@ -316,6 +316,7 @@
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct MicrodroidData {
+ pub salt: Vec<u8>, // Should be [u8; 64] but that isn't serializable.
pub apk_data: ApkData,
pub extra_apks_data: Vec<ApkData>,
pub apex_data: Vec<ApexData>,
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 3d145ee..827f9ff 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -36,6 +36,7 @@
use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
use once_cell::sync::OnceCell;
use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
+use rand::Fill;
use ring::digest;
use rustutils::system_properties;
use rustutils::system_properties::PropertyWatcher;
@@ -195,7 +196,7 @@
authorityHash: authority_hash,
authorityDescriptor: None,
mode: if is_debuggable()? { Mode::DEBUG } else { Mode::NORMAL },
- hidden: [0; 64],
+ hidden: verified_data.salt.try_into().unwrap(),
}])
.context("IDiceMaintenance::demoteSelf failed")?;
Ok(())
@@ -438,9 +439,19 @@
info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
+ // Use the salt from a verified instance, or generate a salt for a new instance.
+ let salt = if let Some(saved_data) = saved_data {
+ saved_data.salt.clone()
+ } else {
+ let mut salt = vec![0u8; 64];
+ salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
+ salt
+ };
+
// At this point, we can ensure that the root_hash from the idsig file is trusted, either by
// fully verifying the APK or by comparing it with the saved root_hash.
Ok(MicrodroidData {
+ salt,
apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
extra_apks_data,
apex_data: apex_data_from_payload,
@@ -471,8 +482,7 @@
let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
loop {
prop.wait()?;
- let val = system_properties::read(APEX_CONFIG_DONE_PROP)?;
- if val == "true" {
+ if system_properties::read_bool(APEX_CONFIG_DONE_PROP, false)? {
break;
}
}
@@ -542,7 +552,7 @@
// Start logging if enabled
// TODO(b/200914564) set filterspec if debug_level is app_only
- if system_properties::read(LOGD_ENABLED_PROP)? == "1" {
+ if system_properties::read_bool(LOGD_ENABLED_PROP, false)? {
system_properties::write("ctl.start", "seriallogging")?;
}
diff --git a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
index 208d61f..f15036c 100644
--- a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
+++ b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
@@ -24,4 +24,7 @@
/* read a system property. */
String readProperty(String prop);
+
+ /* get the VM's stable secret. */
+ byte[] insecurelyExposeSecret();
}
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 4cca538..40d72fe 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -10,6 +10,7 @@
"androidx.test.runner",
"androidx.test.ext.junit",
"com.android.microdroid.testservice-java",
+ "truth-prebuilt",
],
libs: ["android.system.virtualmachine"],
jni_libs: ["MicrodroidTestNativeLib"],
@@ -22,6 +23,7 @@
name: "MicrodroidTestNativeLib",
srcs: ["src/native/testbinary.cpp"],
shared_libs: [
+ "android.security.dice-ndk",
"android.system.virtualmachineservice-ndk",
"com.android.microdroid.testservice-ndk",
"libbase",
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index bd44a3c..803bdc6 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -15,14 +15,14 @@
*/
package com.android.microdroid.test;
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsNot.not;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeNoException;
-import static org.junit.Assume.assumeThat;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
@@ -52,6 +52,7 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@@ -212,8 +213,10 @@
@Test
public void changingDebugLevelInvalidatesVmIdentity()
throws VirtualMachineException, InterruptedException, IOException {
- assumeThat("Skip on Cuttlefish. b/195765441",
- android.os.Build.DEVICE, is(not("vsoc_x86_64")));
+ assume()
+ .withMessage("Skip on Cuttlefish. b/195765441")
+ .that(android.os.Build.DEVICE)
+ .isNotEqualTo("vsoc_x86_64");
VirtualMachineConfig.Builder builder =
new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json");
@@ -269,4 +272,64 @@
};
listener.runToFinish(mInner.mVm);
}
+
+ private byte[] launchVmAndGetSecret(String instanceName)
+ throws VirtualMachineException, InterruptedException {
+ VirtualMachineConfig.Builder builder =
+ new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json");
+ VirtualMachineConfig normalConfig = builder.debugLevel(DebugLevel.NONE).build();
+ mInner.mVm = mInner.mVmm.getOrCreate(instanceName, normalConfig);
+ final CompletableFuture<byte[]> secret = new CompletableFuture<>();
+ VmEventListener listener =
+ new VmEventListener() {
+ @Override
+ public void onPayloadReady(VirtualMachine vm) {
+ try {
+ ITestService testService = ITestService.Stub.asInterface(
+ vm.connectToVsockServer(ITestService.SERVICE_PORT).get());
+ secret.complete(testService.insecurelyExposeSecret());
+ } catch (Exception e) {
+ fail("Exception while connecting to service: " + e.toString());
+ }
+ // TODO(b/208639280): remove this sleep. For now, we need to wait for a few
+ // seconds so that crosvm can actually persist instance.img.
+ try {
+ Thread.sleep(30 * 1000);
+ } catch (InterruptedException e) { }
+ forceStop(vm);
+ }
+ };
+ listener.runToFinish(mInner.mVm);
+ return secret.getNow(null);
+ }
+
+ @Test
+ public void instancesOfSameVmHaveDifferentSecrets()
+ throws VirtualMachineException, InterruptedException {
+ assume()
+ .withMessage("Skip on Cuttlefish. b/195765441")
+ .that(android.os.Build.DEVICE)
+ .isNotEqualTo("vsoc_x86_64");
+
+ byte[] vm_a_secret = launchVmAndGetSecret("test_vm_a");
+ byte[] vm_b_secret = launchVmAndGetSecret("test_vm_b");
+ assertThat(vm_a_secret).isNotNull();
+ assertThat(vm_b_secret).isNotNull();
+ assertThat(vm_a_secret).isNotEqualTo(vm_b_secret);
+ }
+
+ @Test
+ public void sameInstanceKeepsSameSecrets()
+ throws VirtualMachineException, InterruptedException {
+ assume()
+ .withMessage("Skip on Cuttlefish. b/195765441")
+ .that(android.os.Build.DEVICE)
+ .isNotEqualTo("vsoc_x86_64");
+
+ byte[] vm_secret_first_boot = launchVmAndGetSecret("test_vm");
+ byte[] vm_secret_second_boot = launchVmAndGetSecret("test_vm");
+ assertThat(vm_secret_first_boot).isNotNull();
+ assertThat(vm_secret_second_boot).isNotNull();
+ assertThat(vm_secret_first_boot).isEqualTo(vm_secret_second_boot);
+ }
}
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index 301328a..417ff4a 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <aidl/android/security/dice/IDiceNode.h>
#include <aidl/android/system/virtualmachineservice/IVirtualMachineService.h>
#include <aidl/com/android/microdroid/testservice/BnTestService.h>
#include <android-base/file.h>
@@ -32,6 +33,9 @@
#include <binder_rpc_unstable.hpp>
#include <string>
+using aidl::android::hardware::security::dice::BccHandover;
+using aidl::android::security::dice::IDiceNode;
+
using aidl::android::system::virtualmachineservice::IVirtualMachineService;
using android::base::ErrnoError;
@@ -74,6 +78,23 @@
return ndk::ScopedAStatus::ok();
}
+
+ ndk::ScopedAStatus insecurelyExposeSecret(std::vector<uint8_t>* out) override {
+ ndk::SpAIBinder binder(AServiceManager_getService("android.security.dice.IDiceNode"));
+ auto service = IDiceNode::fromBinder(binder);
+ if (service == nullptr) {
+ return ndk::ScopedAStatus::
+ fromServiceSpecificErrorWithMessage(0, "Failed to find diced");
+ }
+ BccHandover handover;
+ auto deriveStatus = service->derive({}, &handover);
+ if (!deriveStatus.isOk()) {
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(0,
+ "Failed call diced");
+ }
+ *out = {handover.cdiSeal.begin(), handover.cdiSeal.end()};
+ return ndk::ScopedAStatus::ok();
+ }
};
auto testService = ndk::SharedRefBase::make<TestService>();