Fix zipfuse race condition
We mount the APK by running zipfuse (asynchronously), and then try to
run the payload from within it. There is a race condition - if the
mount hasn't happened, the payload binary won't appear.
This showed up when I switched to config-less VMs (so we no longer
read the JSON configuration between the two operations) - but only on
cuttlefish, and only in non-debug mode.
Fix it by using a property to signal when the mount has completed.
Bug: 243513572
Test: atest MicrodroidTests (locally & via acloud)
Change-Id: Ib777e7f28afafebd128f8e0c149d485ab9351273
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index b4b2c8b..20fc104 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -71,6 +71,7 @@
const APEX_CONFIG_DONE_PROP: &str = "apex_config.done";
const APP_DEBUGGABLE_PROP: &str = "ro.boot.microdroid.app_debuggable";
+const APK_MOUNT_DONE_PROP: &str = "microdroid_manager.apk.mounted";
// SYNC WITH virtualizationservice/src/crosvm.rs
const FAILURE_SERIAL_DEVICE: &str = "/dev/ttyS1";
@@ -344,12 +345,12 @@
dice_derivation(&verified_data, &payload_metadata)?;
// Before reading a file from the APK, start zipfuse
- let noexec = false;
run_zipfuse(
- noexec,
+ MountForExec::Allowed,
"fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
Path::new("/dev/block/mapper/microdroid-apk"),
Path::new("/mnt/apk"),
+ Some(APK_MOUNT_DONE_PROP),
)
.context("Failed to run zipfuse")?;
@@ -385,6 +386,9 @@
control_service("start", "authfs_service")?;
}
+ // Wait until zipfuse has mounted the APK so we can access the payload
+ wait_for_property_true(APK_MOUNT_DONE_PROP).context("Failed waiting for APK mount done")?;
+
system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
exec_task(task, service)
}
@@ -418,11 +422,25 @@
cmd.spawn().context("Spawn apkdmverity")
}
-fn run_zipfuse(noexec: bool, option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
+enum MountForExec {
+ Allowed,
+ Disallowed,
+}
+
+fn run_zipfuse(
+ noexec: MountForExec,
+ option: &str,
+ zip_path: &Path,
+ mount_dir: &Path,
+ ready_prop: Option<&str>,
+) -> Result<Child> {
let mut cmd = Command::new(ZIPFUSE_BIN);
- if noexec {
+ if let MountForExec::Disallowed = noexec {
cmd.arg("--noexec");
}
+ if let Some(property_name) = ready_prop {
+ cmd.args(["-p", property_name]);
+ }
cmd.arg("-o")
.arg(option)
.arg(zip_path)
@@ -608,12 +626,12 @@
create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
// don't wait, just detach
- let noexec = true;
run_zipfuse(
- noexec,
+ MountForExec::Disallowed,
"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),
+ None,
)
.context("Failed to zipfuse extra apks")?;
}
@@ -623,10 +641,14 @@
// Waits until linker config is generated
fn wait_for_apex_config_done() -> Result<()> {
- let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
+ 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 {
prop.wait()?;
- if system_properties::read_bool(APEX_CONFIG_DONE_PROP, false)? {
+ if system_properties::read_bool(property_name, false)? {
break;
}
}