Merge "Always shutdown the VM when onDeath() os received"
diff --git a/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
index 03f832d..84129b6 100644
--- a/authfs/src/fusefs.rs
+++ b/authfs/src/fusefs.rs
@@ -49,7 +49,10 @@
pub type Inode = u64;
type Handle = u64;
-const DEFAULT_METADATA_TIMEOUT: Duration = Duration::from_secs(5);
+/// Maximum time for a file's metadata to be cached by the kernel. Since any file and directory
+/// changes (if not read-only) has to go through AuthFS to be trusted, the timeout can be maximum.
+const DEFAULT_METADATA_TIMEOUT: Duration = Duration::MAX;
+
const ROOT_INODE: Inode = 1;
/// `AuthFsEntry` defines the filesystem entry type supported by AuthFS.
diff --git a/compos/src/compilation.rs b/compos/src/compilation.rs
index ae4a29d..6049991 100644
--- a/compos/src/compilation.rs
+++ b/compos/src/compilation.rs
@@ -22,12 +22,9 @@
use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
-use std::fs::read_dir;
use std::path::{self, Path, PathBuf};
use std::process::Command;
-use crate::artifact_signer::ArtifactSigner;
-use crate::signing_key::DiceSigner;
use authfs_aidl_interface::aidl::com::android::virt::fs::{
AuthFsConfig::{
AuthFsConfig, InputDirFdAnnotation::InputDirFdAnnotation,
@@ -105,12 +102,15 @@
system_properties::read_bool(name, false).unwrap_or(false)
}
-pub fn odrefresh(
+pub fn odrefresh<F>(
odrefresh_path: &Path,
context: OdrefreshContext,
authfs_service: Strong<dyn IAuthFsService>,
- signer: DiceSigner,
-) -> Result<ExitCode> {
+ success_fn: F,
+) -> Result<ExitCode>
+where
+ F: FnOnce(PathBuf) -> Result<()>,
+{
// Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
// is out of scope.
let authfs_config = AuthFsConfig {
@@ -183,13 +183,8 @@
info!("odrefresh exited with {:?}", exit_code);
if exit_code == ExitCode::CompilationSuccess {
- // authfs only shows us the files we created, so it's ok to just sign everything under
- // the target directory.
let target_dir = art_apex_data.join(context.target_dir_name);
- let mut artifact_signer = ArtifactSigner::new(&target_dir);
- add_artifacts(&target_dir, &mut artifact_signer)?;
-
- artifact_signer.write_info_and_signature(signer, &target_dir.join("compos.info"))?;
+ success_fn(target_dir)?;
}
Ok(exit_code)
@@ -245,24 +240,6 @@
Ok(())
}
-fn add_artifacts(target_dir: &Path, artifact_signer: &mut ArtifactSigner) -> Result<()> {
- for entry in
- read_dir(&target_dir).with_context(|| format!("Traversing {}", target_dir.display()))?
- {
- let entry = entry?;
- let file_type = entry.file_type()?;
- if file_type.is_dir() {
- add_artifacts(&entry.path(), artifact_signer)?;
- } else if file_type.is_file() {
- artifact_signer.add_artifact(&entry.path())?;
- } else {
- // authfs shouldn't create anything else, but just in case
- bail!("Unexpected file type in artifacts: {:?}", entry);
- }
- }
- Ok(())
-}
-
fn spawn_jailed_task(executable: &Path, args: &[String], env_vars: &[String]) -> Result<Minijail> {
// TODO(b/185175567): Run in a more restricted sandbox.
let jail = Minijail::new()?;
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index 3ec15dd..5f3ee62 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -18,17 +18,19 @@
//! file descriptors backed by authfs (via authfs_service) and pass the file descriptors to the
//! actual compiler.
-use anyhow::{Context, Result};
+use anyhow::{bail, Context, Result};
use binder_common::new_binder_exception;
use compos_common::binder::to_binder_result;
use log::warn;
use std::default::Default;
-use std::path::PathBuf;
+use std::fs::read_dir;
+use std::path::{Path, PathBuf};
use std::sync::RwLock;
+use crate::artifact_signer::ArtifactSigner;
use crate::compilation::{odrefresh, OdrefreshContext};
use crate::dice::Dice;
-use crate::signing_key::{DiceSigner, DiceSigningKey};
+use crate::signing_key::DiceSigningKey;
use authfs_aidl_interface::aidl::com::android::virt::fs::IAuthFsService::IAuthFsService;
use compos_aidl_interface::aidl::com::android::compos::{
CompOsKeyData::CompOsKeyData,
@@ -57,17 +59,6 @@
key_blob: RwLock<Vec<u8>>,
}
-impl CompOsService {
- fn new_signer(&self) -> BinderResult<DiceSigner> {
- let key = &*self.key_blob.read().unwrap();
- if key.is_empty() {
- Err(new_binder_exception(ExceptionCode::ILLEGAL_STATE, "Key is not initialized"))
- } else {
- to_binder_result(self.signing_key.new_signer(key))
- }
- }
-}
-
impl Interface for CompOsService {}
impl ICompOsService for CompOsService {
@@ -91,6 +82,14 @@
zygote_arch: &str,
system_server_compiler_filter: &str,
) -> BinderResult<i8> {
+ let key = &*self.key_blob.read().unwrap();
+ if key.is_empty() {
+ return Err(new_binder_exception(
+ ExceptionCode::ILLEGAL_STATE,
+ "Key is not initialized",
+ ));
+ }
+
let context = to_binder_result(OdrefreshContext::new(
compilation_mode,
system_dir_fd,
@@ -103,8 +102,16 @@
let authfs_service = get_authfs_service()?;
let exit_code = to_binder_result(
- odrefresh(&self.odrefresh_path, context, authfs_service, self.new_signer()?)
- .context("odrefresh failed"),
+ odrefresh(&self.odrefresh_path, context, authfs_service, |output_dir| {
+ // authfs only shows us the files we created, so it's ok to just sign everything
+ // under the output directory.
+ let mut artifact_signer = ArtifactSigner::new(&output_dir);
+ add_artifacts(&output_dir, &mut artifact_signer)?;
+
+ let signer = to_binder_result(self.signing_key.new_signer(key))?;
+ artifact_signer.write_info_and_signature(signer, &output_dir.join("compos.info"))
+ })
+ .context("odrefresh failed"),
)?;
Ok(exit_code as i8)
}
@@ -126,3 +133,21 @@
fn get_authfs_service() -> BinderResult<Strong<dyn IAuthFsService>> {
Ok(authfs_aidl_interface::binder::get_interface(AUTHFS_SERVICE_NAME)?)
}
+
+fn add_artifacts(target_dir: &Path, artifact_signer: &mut ArtifactSigner) -> Result<()> {
+ for entry in
+ read_dir(&target_dir).with_context(|| format!("Traversing {}", target_dir.display()))?
+ {
+ let entry = entry?;
+ let file_type = entry.file_type()?;
+ if file_type.is_dir() {
+ add_artifacts(&entry.path(), artifact_signer)?;
+ } else if file_type.is_file() {
+ artifact_signer.add_artifact(&entry.path())?;
+ } else {
+ // authfs shouldn't create anything else, but just in case
+ bail!("Unexpected file type in artifacts: {:?}", entry);
+ }
+ }
+ Ok(())
+}
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index bc8a4a5..10bcbf4 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -16,5 +16,9 @@
"VirtualizationTestHelper",
],
per_testcase_directory: true,
- data: [":MicrodroidTestApp"],
+ data: [
+ ":MicrodroidTestApp",
+ ":microdroid_general_sepolicy.conf",
+ ],
+ data_native_bins: ["sepolicy-analyze"],
}
diff --git a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
index e15f1ae..e3f1968 100644
--- a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
+++ b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
@@ -149,9 +149,26 @@
}
public static CommandResult runOnMicrodroidForResult(String... cmd) {
- final long timeout = 30000; // 30 sec. Microdroid is extremely slow on GCE-on-CF.
+ final long timeoutMs = 30000; // 30 sec. Microdroid is extremely slow on GCE-on-CF.
return RunUtil.getDefault()
- .runTimedCmd(timeout, "adb", "-s", MICRODROID_SERIAL, "shell", join(cmd));
+ .runTimedCmd(timeoutMs, "adb", "-s", MICRODROID_SERIAL, "shell", join(cmd));
+ }
+
+ public static void pullMicrodroidFile(String path, File target) {
+ final long timeoutMs = 30000; // 30 sec. Microdroid is extremely slow on GCE-on-CF.
+ CommandResult result =
+ RunUtil.getDefault()
+ .runTimedCmd(
+ timeoutMs,
+ "adb",
+ "-s",
+ MICRODROID_SERIAL,
+ "pull",
+ path,
+ target.getPath());
+ if (result.getStatus() != CommandStatus.SUCCESS) {
+ fail("pulling " + path + " has failed: " + result);
+ }
}
// Asserts the command will fail on Microdroid.
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index 6aa7566..25adc40 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -17,18 +17,24 @@
package android.virt.test;
import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.RunUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.File;
import java.util.Optional;
@RunWith(DeviceJUnit4ClassRunner.class)
@@ -43,6 +49,16 @@
private static final int NUM_VCPUS = 3;
private static final String CPU_AFFINITY = "0,1,2";
+ // TODO(b/176805428): remove this
+ private boolean isCuttlefish() throws Exception {
+ String productName = getDevice().getProperty("ro.product.name");
+ return (null != productName)
+ && (productName.startsWith("aosp_cf_x86")
+ || productName.startsWith("aosp_cf_arm")
+ || productName.startsWith("cf_x86")
+ || productName.startsWith("cf_arm"));
+ }
+
private int minMemorySize() throws DeviceNotAvailableException {
CommandRunner android = new CommandRunner(getDevice());
String abi = android.run("getprop", "ro.product.cpu.abi");
@@ -103,6 +119,31 @@
assertThat(runOnMicrodroid("cat /proc/cpuinfo | grep processor | wc -l"),
is(Integer.toString(NUM_VCPUS)));
+ // TODO(b/176805428): adb is broken for nested VM
+ if (!isCuttlefish()) {
+ // Check neverallow rules on microdroid
+ File policyFile = FileUtil.createTempFile("microdroid_sepolicy", "");
+ pullMicrodroidFile("/sys/fs/selinux/policy", policyFile);
+
+ File generalPolicyConfFile = findTestFile("microdroid_general_sepolicy.conf");
+ File sepolicyAnalyzeBin = findTestFile("sepolicy-analyze");
+
+ CommandResult result =
+ RunUtil.getDefault()
+ .runTimedCmd(
+ 10000,
+ sepolicyAnalyzeBin.getPath(),
+ policyFile.getPath(),
+ "neverallow",
+ "-w",
+ "-f",
+ generalPolicyConfFile.getPath());
+ assertEquals(
+ "neverallow check failed: " + result.getStderr().trim(),
+ result.getStatus(),
+ CommandStatus.SUCCESS);
+ }
+
shutdownMicrodroid(getDevice(), cid);
}