Merge changes I0d736955,I62345041,I563036b4 into main
* changes:
Start apexd later
Minor changes to boot sequence
Microdroid Mananger main.rs refactoring
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 7ba54f8..1b41e58 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -34,7 +34,7 @@
use crate::dice::dice_derivation;
use crate::dice_driver::DiceDriver;
-use crate::instance::{ApexData, InstanceDisk, MicrodroidData};
+use crate::instance::{InstanceDisk, MicrodroidData};
use crate::verify::verify_payload;
use crate::vm_payload_service::register_vm_payload_service;
use anyhow::{anyhow, bail, ensure, Context, Error, Result};
@@ -42,10 +42,10 @@
use keystore2_crypto::ZVec;
use libc::VMADDR_CID_HOST;
use log::{error, info};
-use microdroid_metadata::{write_metadata, PayloadMetadata};
+use microdroid_metadata::PayloadMetadata;
use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
use nix::sys::signal::Signal;
-use payload::{load_metadata, to_metadata};
+use payload::load_metadata;
use rpcbinder::RpcSession;
use rustutils::sockets::android_get_control_socket;
use rustutils::system_properties;
@@ -143,15 +143,6 @@
Ok(())
}
-fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
- // The host is running a VirtualMachineService for this VM on a port equal
- // to the CID of this VM.
- let port = vsock::get_local_cid().context("Could not determine local CID")?;
- RpcSession::new()
- .setup_vsock_client(VMADDR_CID_HOST, port)
- .context("Could not connect to IVirtualMachineService")
-}
-
fn main() -> Result<()> {
// If debuggable, print full backtrace to console log with stdio_to_kmsg
if is_debuggable()? {
@@ -174,25 +165,6 @@
})
}
-/// Prepares a socket file descriptor for the vm payload service.
-///
-/// # Safety
-///
-/// The caller must ensure that this function is the only place that claims ownership
-/// of the file descriptor and it is called only once.
-unsafe fn prepare_vm_payload_service_socket() -> Result<OwnedFd> {
- let raw_fd = android_get_control_socket(VM_PAYLOAD_SERVICE_SOCKET_NAME)?;
-
- // Creating OwnedFd for stdio FDs is not safe.
- if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
- bail!("File descriptor {raw_fd} is standard I/O descriptor");
- }
- // SAFETY: Initializing OwnedFd for a RawFd created by the init.
- // We checked that the integer value corresponds to a valid FD and that the caller
- // ensures that this is the only place to claim its ownership.
- Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
-}
-
fn try_main() -> Result<()> {
android_logger::init_once(
android_logger::Config::default()
@@ -245,71 +217,6 @@
}
}
-fn post_payload_work() -> Result<()> {
- // Sync the encrypted storage filesystem (flushes the filesystem caches).
- if Path::new(ENCRYPTEDSTORE_BACKING_DEVICE).exists() {
- let mountpoint = CString::new(ENCRYPTEDSTORE_MOUNTPOINT).unwrap();
-
- // SAFETY: `mountpoint` is a valid C string. `syncfs` and `close` are safe for any parameter
- // values.
- let ret = unsafe {
- let dirfd = libc::open(
- mountpoint.as_ptr(),
- libc::O_DIRECTORY | libc::O_RDONLY | libc::O_CLOEXEC,
- );
- ensure!(dirfd >= 0, "Unable to open {:?}", mountpoint);
- let ret = libc::syncfs(dirfd);
- libc::close(dirfd);
- ret
- };
- if ret != 0 {
- error!("failed to sync encrypted storage.");
- return Err(anyhow!(std::io::Error::last_os_error()));
- }
- }
- Ok(())
-}
-
-fn is_strict_boot() -> bool {
- Path::new(AVF_STRICT_BOOT).exists()
-}
-
-fn is_new_instance() -> bool {
- Path::new(AVF_NEW_INSTANCE).exists()
-}
-
-fn is_verified_boot() -> bool {
- !Path::new(DEBUG_MICRODROID_NO_VERIFIED_BOOT).exists()
-}
-
-fn is_debuggable() -> Result<bool> {
- Ok(system_properties::read_bool(DEBUGGABLE_PROP, true)?)
-}
-
-fn should_export_tombstones(config: &VmPayloadConfig) -> bool {
- match config.export_tombstones {
- Some(b) => b,
- None => is_debuggable().unwrap_or(false),
- }
-}
-
-/// Get debug policy value in bool. It's true iff the value is explicitly set to <1>.
-fn get_debug_policy_bool(path: &'static str) -> Result<Option<bool>> {
- let mut file = match File::open(path) {
- Ok(dp) => dp,
- Err(e) => {
- info!(
- "Assumes that debug policy is disabled because failed to read debug policy ({e:?})"
- );
- return Ok(Some(false));
- }
- };
- let mut log: [u8; 4] = Default::default();
- file.read_exact(&mut log).context("Malformed data in {path}")?;
- // DT spec uses big endian although Android is always little endian.
- Ok(Some(u32::from_be_bytes(log) == 1))
-}
-
fn try_run_payload(
service: &Strong<dyn IVirtualMachineService>,
vm_payload_service_fd: OwnedFd,
@@ -377,6 +284,13 @@
let dice_artifacts = dice_derivation(dice, &verified_data, &payload_metadata)?;
let vm_secret = VmSecret::new(dice_artifacts).context("Failed to create VM secrets")?;
+ if cfg!(dice_changes) {
+ // Now that the DICE derivation is done, it's ok to allow payload code to run.
+
+ // Start apexd to activate APEXes. This may allow code within them to run.
+ system_properties::write("ctl.start", "apexd-vm")?;
+ }
+
// Run encryptedstore binary to prepare the storage
let encryptedstore_child = if Path::new(ENCRYPTEDSTORE_BACKING_DEVICE).exists() {
info!("Preparing encryptedstore ...");
@@ -419,10 +333,12 @@
);
mount_extra_apks(&config, &mut zipfuse)?;
- // Wait until apex config is done. (e.g. linker configuration for apexes)
- wait_for_apex_config_done()?;
-
- setup_config_sysprops(&config)?;
+ register_vm_payload_service(
+ allow_restricted_apis,
+ service.clone(),
+ vm_secret,
+ vm_payload_service_fd,
+ )?;
// Set export_tombstones if enabled
if should_export_tombstones(&config) {
@@ -431,16 +347,20 @@
.context("set microdroid_manager.export_tombstones.enabled")?;
}
+ // Wait until apex config is done. (e.g. linker configuration for apexes)
+ wait_for_property_true(APEX_CONFIG_DONE_PROP).context("Failed waiting for apex config done")?;
+
+ // Trigger init post-fs-data. This will start authfs if we wask it to.
+ if config.enable_authfs {
+ system_properties::write("microdroid_manager.authfs.enabled", "1")
+ .context("failed to write microdroid_manager.authfs.enabled")?;
+ }
+ system_properties::write("microdroid_manager.config_done", "1")
+ .context("failed to write microdroid_manager.config_done")?;
+
// Wait until zipfuse has mounted the APKs so we can access the payload
zipfuse.wait_until_done()?;
- register_vm_payload_service(
- allow_restricted_apis,
- service.clone(),
- vm_secret,
- vm_payload_service_fd,
- )?;
-
// Wait for encryptedstore to finish mounting the storage (if enabled) before setting
// microdroid_manager.init_done. Reason is init stops uneventd after that.
// Encryptedstore, however requires ueventd
@@ -449,7 +369,10 @@
ensure!(exitcode.success(), "Unable to prepare encrypted storage. Exitcode={}", exitcode);
}
+ // Wait for init to have finished booting.
wait_for_property_true("dev.bootcomplete").context("failed waiting for dev.bootcomplete")?;
+
+ // And then tell it we're done so unnecessary services can be shut down.
system_properties::write("microdroid_manager.init_done", "1")
.context("set microdroid_manager.init_done")?;
@@ -457,6 +380,120 @@
exec_task(task, service).context("Failed to run payload")
}
+fn post_payload_work() -> Result<()> {
+ // Sync the encrypted storage filesystem (flushes the filesystem caches).
+ if Path::new(ENCRYPTEDSTORE_BACKING_DEVICE).exists() {
+ let mountpoint = CString::new(ENCRYPTEDSTORE_MOUNTPOINT).unwrap();
+
+ // SAFETY: `mountpoint` is a valid C string. `syncfs` and `close` are safe for any parameter
+ // values.
+ let ret = unsafe {
+ let dirfd = libc::open(
+ mountpoint.as_ptr(),
+ libc::O_DIRECTORY | libc::O_RDONLY | libc::O_CLOEXEC,
+ );
+ ensure!(dirfd >= 0, "Unable to open {:?}", mountpoint);
+ let ret = libc::syncfs(dirfd);
+ libc::close(dirfd);
+ ret
+ };
+ if ret != 0 {
+ error!("failed to sync encrypted storage.");
+ return Err(anyhow!(std::io::Error::last_os_error()));
+ }
+ }
+ Ok(())
+}
+
+fn mount_extra_apks(config: &VmPayloadConfig, zipfuse: &mut Zipfuse) -> Result<()> {
+ // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
+ for i in 0..config.extra_apks.len() {
+ let mount_dir = format!("/mnt/extra-apk/{i}");
+ create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
+
+ let mount_for_exec =
+ if cfg!(multi_tenant) { MountForExec::Allowed } else { MountForExec::Disallowed };
+ // These run asynchronously in parallel - we wait later for them to complete.
+ zipfuse.mount(
+ mount_for_exec,
+ "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
+ Path::new(&format!("/dev/block/mapper/extra-apk-{i}")),
+ Path::new(&mount_dir),
+ format!("microdroid_manager.extra_apk.mounted.{i}"),
+ )?;
+ }
+
+ Ok(())
+}
+
+fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
+ // The host is running a VirtualMachineService for this VM on a port equal
+ // to the CID of this VM.
+ let port = vsock::get_local_cid().context("Could not determine local CID")?;
+ RpcSession::new()
+ .setup_vsock_client(VMADDR_CID_HOST, port)
+ .context("Could not connect to IVirtualMachineService")
+}
+
+/// Prepares a socket file descriptor for the vm payload service.
+///
+/// # Safety
+///
+/// The caller must ensure that this function is the only place that claims ownership
+/// of the file descriptor and it is called only once.
+unsafe fn prepare_vm_payload_service_socket() -> Result<OwnedFd> {
+ let raw_fd = android_get_control_socket(VM_PAYLOAD_SERVICE_SOCKET_NAME)?;
+
+ // Creating OwnedFd for stdio FDs is not safe.
+ if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
+ bail!("File descriptor {raw_fd} is standard I/O descriptor");
+ }
+ // SAFETY: Initializing OwnedFd for a RawFd created by the init.
+ // We checked that the integer value corresponds to a valid FD and that the caller
+ // ensures that this is the only place to claim its ownership.
+ Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
+}
+
+fn is_strict_boot() -> bool {
+ Path::new(AVF_STRICT_BOOT).exists()
+}
+
+fn is_new_instance() -> bool {
+ Path::new(AVF_NEW_INSTANCE).exists()
+}
+
+fn is_verified_boot() -> bool {
+ !Path::new(DEBUG_MICRODROID_NO_VERIFIED_BOOT).exists()
+}
+
+fn is_debuggable() -> Result<bool> {
+ Ok(system_properties::read_bool(DEBUGGABLE_PROP, true)?)
+}
+
+fn should_export_tombstones(config: &VmPayloadConfig) -> bool {
+ match config.export_tombstones {
+ Some(b) => b,
+ None => is_debuggable().unwrap_or(false),
+ }
+}
+
+/// Get debug policy value in bool. It's true iff the value is explicitly set to <1>.
+fn get_debug_policy_bool(path: &'static str) -> Result<Option<bool>> {
+ let mut file = match File::open(path) {
+ Ok(dp) => dp,
+ Err(e) => {
+ info!(
+ "Assumes that debug policy is disabled because failed to read debug policy ({e:?})"
+ );
+ return Ok(Some(false));
+ }
+ };
+ let mut log: [u8; 4] = Default::default();
+ file.read_exact(&mut log).context("Malformed data in {path}")?;
+ // DT spec uses big endian although Android is always little endian.
+ Ok(Some(u32::from_be_bytes(log) == 1))
+}
+
enum MountForExec {
Allowed,
Disallowed,
@@ -504,65 +541,6 @@
}
}
-fn write_apex_payload_data(
- saved_data: Option<&MicrodroidData>,
- apex_data_from_payload: &[ApexData],
-) -> Result<()> {
- if let Some(saved_apex_data) = saved_data.map(|d| &d.apex_data) {
- // We don't support APEX updates. (assuming that update will change root digest)
- ensure!(
- saved_apex_data == apex_data_from_payload,
- MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
- );
- let apex_metadata = to_metadata(apex_data_from_payload);
- // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
- // metadata instead of the default one (/dev/block/by-name/payload-metadata)
- OpenOptions::new()
- .create_new(true)
- .write(true)
- .open("/apex/vm-payload-metadata")
- .context("Failed to open /apex/vm-payload-metadata")
- .and_then(|f| write_metadata(&apex_metadata, f))?;
- }
- Ok(())
-}
-
-fn mount_extra_apks(config: &VmPayloadConfig, zipfuse: &mut Zipfuse) -> Result<()> {
- // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
- for i in 0..config.extra_apks.len() {
- let mount_dir = format!("/mnt/extra-apk/{i}");
- create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
-
- let mount_for_exec =
- if cfg!(multi_tenant) { MountForExec::Allowed } else { MountForExec::Disallowed };
- // These run asynchronously in parallel - we wait later for them to complete.
- zipfuse.mount(
- mount_for_exec,
- "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
- Path::new(&format!("/dev/block/mapper/extra-apk-{i}")),
- Path::new(&mount_dir),
- format!("microdroid_manager.extra_apk.mounted.{i}"),
- )?;
- }
-
- Ok(())
-}
-
-fn setup_config_sysprops(config: &VmPayloadConfig) -> Result<()> {
- if config.enable_authfs {
- system_properties::write("microdroid_manager.authfs.enabled", "1")
- .context("failed to write microdroid_manager.authfs.enabled")?;
- }
- system_properties::write("microdroid_manager.config_done", "1")
- .context("failed to write microdroid_manager.config_done")?;
- Ok(())
-}
-
-// Waits until linker config is generated
-fn wait_for_apex_config_done() -> Result<()> {
- wait_for_property_true(APEX_CONFIG_DONE_PROP).context("Failed waiting for apex config done")
-}
-
fn wait_for_property_true(property_name: &str) -> Result<()> {
let mut prop = PropertyWatcher::new(property_name)?;
loop {
diff --git a/microdroid_manager/src/verify.rs b/microdroid_manager/src/verify.rs
index 22f3414..e63530b 100644
--- a/microdroid_manager/src/verify.rs
+++ b/microdroid_manager/src/verify.rs
@@ -12,18 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use crate::instance::{ApkData, MicrodroidData, RootHash};
-use crate::payload::get_apex_data_from_payload;
-use crate::{is_strict_boot, is_verified_boot, write_apex_payload_data, MicrodroidError};
+use crate::instance::{ApexData, ApkData, MicrodroidData, RootHash};
+use crate::payload::{get_apex_data_from_payload, to_metadata};
+use crate::{is_strict_boot, is_verified_boot, MicrodroidError};
use anyhow::{anyhow, ensure, Context, Result};
use apkmanifest::get_manifest_info;
use apkverify::{get_public_key_der, verify, V4Signature};
use glob::glob;
use itertools::sorted;
use log::{info, warn};
-use microdroid_metadata::Metadata;
+use microdroid_metadata::{write_metadata, Metadata};
use rand::Fill;
use rustutils::system_properties;
+use std::fs::OpenOptions;
use std::path::Path;
use std::process::{Child, Command};
use std::str;
@@ -134,8 +135,10 @@
write_apex_payload_data(saved_data, &apex_data_from_payload)?;
}
- // Start apexd to activate APEXes
- system_properties::write("ctl.start", "apexd-vm")?;
+ if cfg!(not(dice_changes)) {
+ // Start apexd to activate APEXes
+ system_properties::write("ctl.start", "apexd-vm")?;
+ }
// TODO(inseob): add timeout
apkdmverity_child.wait()?;
@@ -207,6 +210,29 @@
})
}
+fn write_apex_payload_data(
+ saved_data: Option<&MicrodroidData>,
+ apex_data_from_payload: &[ApexData],
+) -> Result<()> {
+ if let Some(saved_apex_data) = saved_data.map(|d| &d.apex_data) {
+ // We don't support APEX updates. (assuming that update will change root digest)
+ ensure!(
+ saved_apex_data == apex_data_from_payload,
+ MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
+ );
+ let apex_metadata = to_metadata(apex_data_from_payload);
+ // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
+ // metadata instead of the default one (/dev/block/by-name/payload-metadata)
+ OpenOptions::new()
+ .create_new(true)
+ .write(true)
+ .open("/apex/vm-payload-metadata")
+ .context("Failed to open /apex/vm-payload-metadata")
+ .and_then(|f| write_metadata(&apex_metadata, f))?;
+ }
+ Ok(())
+}
+
fn get_apk_root_hash_from_idsig<P: AsRef<Path>>(idsig_path: P) -> Result<Box<RootHash>> {
Ok(V4Signature::from_idsig_path(idsig_path)?.hashing_info.raw_root_hash)
}