Merge changes I22ebac41,Ib81cfb24
* changes:
Update kernel to builds 8798673
Update kernel to builds 8798673
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 0ca6211..2d3f6ff 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -44,6 +44,9 @@
"path": "packages/modules/Virtualization/authfs"
},
{
+ "path": "packages/modules/Virtualization/vmbase"
+ },
+ {
"path": "packages/modules/Virtualization/zipfuse"
}
]
diff --git a/docs/getting_started/pixel6.md b/docs/getting_started/pixel6.md
new file mode 100644
index 0000000..82391c4
--- /dev/null
+++ b/docs/getting_started/pixel6.md
@@ -0,0 +1,129 @@
+# Instructions for building custom AVF on Pixel 6 or 6 Pro
+
+This document provides steps for building AVF from AOSP, and then install it to
+Pixel 6 series to better understand AVF and do some experiments.
+
+**WARNING**: Unless Android 13 is released to AOSP (expected to be at Summer
+2022, exact date TBD) by the time when you read this documentation, or you or
+your company have early access to Android Tiramisu source tree, you **CANNOT**
+follow this instruction. In that case, you can only **USE** the AVF that is
+shipped in the Android 13 Beta Image.
+
+This is because AVF in the beta image is signed by Google and therefore it can't
+be updated to a new AVF built in AOSP which can't be signed by the Google key
+that is not shared with AOSP.
+
+## Upgrade to Android 13 Beta Image
+
+First, upgrade your Pixel 6 or Pixel 6 Pro to the Android 13 Beta Image. This
+can be done in two ways:
+
+* Join [Android Beta Program](https://www.google.com/android/beta) and then OTA
+ to Android 13.
+* Manually flash [Android 13 Beta Images](https://developer.android.com/about/versions/13/download#factory-images).
+
+Then enable ADB debugging in "Settings" -> "System" -> "Developer options".
+Finally, enable PKVM.
+
+```shell
+adb reboot bootloader
+fastboot flashing unlock
+fastboot oem pkvm enable
+fastboot reboot
+```
+
+## Building GSI and flashing it
+
+Prepare your Android 13 (Tiramisu) source tree.
+
+```shell
+mkdir tm
+cd tm
+repo init -u <URL> -m <your_tm_branch>
+repo sync -c --no-tags -j 10
+```
+
+Patch GSI so that it includes AVF. Edit
+`build/make/target/product/gsi_release.mk` and add the following line to the
+end (or anywhere in the file that makes sense):
+
+```
+PRODUCT_PACKAGES += com.android.virt
+```
+
+Build GSI.
+
+```shell
+source build/envsetup.sh
+choosecombo 1 aosp_arm64 userdebug
+m
+```
+
+Flash GSI to the Pixel device.
+
+```shell
+adb reboot bootloader
+fastboot reboot fastboot
+fastboot delete-logical-partition product_a
+fastboot flash system out/target/product/generic_arm64/system.img
+fastboot --disable-verification flash vbmeta out/target/product/generic_arm64/vbmeta.img
+fastboot -w reboot
+```
+
+Deleting the logical partition `product_a` is needed because the GSI image is
+bigger than the logical partition `system_a` of the beta image.
+`--disable-verification` when flashing the `vbmeta` partition is critical. Don't
+miss it.
+
+Lastly, check if you are running GSI.
+
+```shell
+adb shell getprop ro.build.product
+adb shell ls /dev/kvm
+adb shell ls /apex/com.android.virt/bin/vm
+```
+
+The result should be as follows.
+
+```
+generic_arm64
+/dev/kvm
+/apex/com.android.virt/bin/vm
+```
+
+## Building and installing AVF from AOSP
+
+Checkout AOSP master branch.
+
+```shell
+mkdir aosp
+cd aosp
+repo init -u https://android.googlesource.com/platform/manifest -b master
+repo sync -c --no-tags -j 10
+```
+
+Then build the `com.android.virt` APEX.
+
+```shell
+source build/envsetup.sh
+banchan com.android.virt aosp_arm64
+UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true m apps_only dist
+```
+
+Install the newly built AVF to the device
+
+```shell
+adb install out/dist/com.android.virt.apex
+adb reboot
+```
+
+If this doesn't work for some reason, try this:
+
+```
+adb root
+adb shell setenforce 0
+adb push out/dist/com.android.virt.apex /data/local/
+adb shell cmd -w apexservice deactivatePackage /system/system_ext/apex/com.android.virt.apex
+adb shell cmd -w apexservice activatePackage /data/local/com.android.virt.apex
+// Don't adb reboot
+```
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 15d6663..350fbc5 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -299,7 +299,9 @@
dice_derivation(&verified_data, &metadata.payload_config_path)?;
// Before reading a file from the APK, start zipfuse
+ let noexec = false;
run_zipfuse(
+ noexec,
"fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
Path::new("/dev/block/mapper/microdroid-apk"),
Path::new("/mnt/apk"),
@@ -364,9 +366,12 @@
cmd.spawn().context("Spawn apkdmverity")
}
-fn run_zipfuse(option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
- Command::new(ZIPFUSE_BIN)
- .arg("-o")
+fn run_zipfuse(noexec: bool, option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
+ let mut cmd = Command::new(ZIPFUSE_BIN);
+ if noexec {
+ cmd.arg("--noexec");
+ }
+ cmd.arg("-o")
.arg(option)
.arg(zip_path)
.arg(mount_dir)
@@ -537,7 +542,9 @@
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,
"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),
diff --git a/vmbase/TEST_MAPPING b/vmbase/TEST_MAPPING
new file mode 100644
index 0000000..9b7e4cb
--- /dev/null
+++ b/vmbase/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "vmbase_example.integration_test"
+ }
+ ]
+}
diff --git a/zipfuse/src/main.rs b/zipfuse/src/main.rs
index c3fae69..874056a 100644
--- a/zipfuse/src/main.rs
+++ b/zipfuse/src/main.rs
@@ -46,6 +46,12 @@
.required(false)
.help("Comma separated list of mount options"),
)
+ .arg(
+ Arg::with_name("noexec")
+ .long("noexec")
+ .takes_value(false)
+ .help("Disallow the execution of binary files"),
+ )
.arg(Arg::with_name("ZIPFILE").required(true))
.arg(Arg::with_name("MOUNTPOINT").required(true))
.get_matches();
@@ -53,12 +59,18 @@
let zip_file = matches.value_of("ZIPFILE").unwrap().as_ref();
let mount_point = matches.value_of("MOUNTPOINT").unwrap().as_ref();
let options = matches.value_of("options");
- run_fuse(zip_file, mount_point, options)?;
+ let noexec = matches.is_present("noexec");
+ run_fuse(zip_file, mount_point, options, noexec)?;
Ok(())
}
/// Runs a fuse filesystem by mounting `zip_file` on `mount_point`.
-pub fn run_fuse(zip_file: &Path, mount_point: &Path, extra_options: Option<&str>) -> Result<()> {
+pub fn run_fuse(
+ zip_file: &Path,
+ mount_point: &Path,
+ extra_options: Option<&str>,
+ noexec: bool,
+) -> Result<()> {
const MAX_READ: u32 = 1 << 20; // TODO(jiyong): tune this
const MAX_WRITE: u32 = 1 << 13; // This is a read-only filesystem
@@ -76,12 +88,12 @@
mount_options.push(MountOption::Extra(value));
}
- fuse::mount(
- mount_point,
- "zipfuse",
- libc::MS_NOSUID | libc::MS_NODEV | libc::MS_RDONLY,
- &mount_options,
- )?;
+ let mut mount_flags = libc::MS_NOSUID | libc::MS_NODEV | libc::MS_RDONLY;
+ if noexec {
+ mount_flags |= libc::MS_NOEXEC;
+ }
+
+ fuse::mount(mount_point, "zipfuse", mount_flags, &mount_options)?;
let mut config = fuse::FuseConfig::new();
config.dev_fuse(dev_fuse).max_write(MAX_WRITE).max_read(MAX_READ);
Ok(config.enter_message_loop(ZipFuse::new(zip_file)?)?)
@@ -435,22 +447,28 @@
use zip::write::FileOptions;
#[cfg(not(target_os = "android"))]
- fn start_fuse(zip_path: &Path, mnt_path: &Path) {
+ fn start_fuse(zip_path: &Path, mnt_path: &Path, noexec: bool) {
let zip_path = PathBuf::from(zip_path);
let mnt_path = PathBuf::from(mnt_path);
std::thread::spawn(move || {
- crate::run_fuse(&zip_path, &mnt_path, None).unwrap();
+ crate::run_fuse(&zip_path, &mnt_path, None, noexec).unwrap();
});
}
#[cfg(target_os = "android")]
- fn start_fuse(zip_path: &Path, mnt_path: &Path) {
+ fn start_fuse(zip_path: &Path, mnt_path: &Path, noexec: bool) {
// Note: for some unknown reason, running a thread to serve fuse doesn't work on Android.
// Explicitly spawn a zipfuse process instead.
// TODO(jiyong): fix this
+ let noexec = if noexec { "--noexec" } else { "" };
assert!(std::process::Command::new("sh")
.arg("-c")
- .arg(format!("/data/local/tmp/zipfuse {} {}", zip_path.display(), mnt_path.display()))
+ .arg(format!(
+ "/data/local/tmp/zipfuse {} {} {}",
+ noexec,
+ zip_path.display(),
+ mnt_path.display()
+ ))
.spawn()
.is_ok());
}
@@ -476,6 +494,14 @@
// Creates a zip file, adds some files to the zip file, mounts it using zipfuse, runs the check
// routine, and finally unmounts.
fn run_test(add: fn(&mut zip::ZipWriter<File>), check: fn(&std::path::Path)) {
+ run_test_noexec(false, add, check);
+ }
+
+ fn run_test_noexec(
+ noexec: bool,
+ add: fn(&mut zip::ZipWriter<File>),
+ check: fn(&std::path::Path),
+ ) {
// Create an empty zip file
let test_dir = tempfile::TempDir::new().unwrap();
let zip_path = test_dir.path().join("test.zip");
@@ -492,7 +518,7 @@
let mnt_path = test_dir.path().join("mnt");
assert!(fs::create_dir(&mnt_path).is_ok());
- start_fuse(&zip_path, &mnt_path);
+ start_fuse(&zip_path, &mnt_path, noexec);
let mnt_path = test_dir.path().join("mnt");
// Give some time for the fuse to boot up
@@ -577,6 +603,26 @@
}
#[test]
+ fn noexec() {
+ fn add_executable(zip: &mut zip::ZipWriter<File>) {
+ zip.start_file("executable", FileOptions::default().unix_permissions(0o755)).unwrap();
+ }
+
+ // Executables can be run when not mounting with noexec.
+ run_test(add_executable, |root| {
+ let res = std::process::Command::new(root.join("executable")).status();
+ res.unwrap();
+ });
+
+ // Mounting with noexec results in permissions denial when running an executable.
+ let noexec = true;
+ run_test_noexec(noexec, add_executable, |root| {
+ let res = std::process::Command::new(root.join("executable")).status();
+ assert!(matches!(res.unwrap_err().kind(), std::io::ErrorKind::PermissionDenied));
+ });
+ }
+
+ #[test]
fn single_dir() {
run_test(
|zip| {
@@ -688,7 +734,8 @@
let mnt_path = test_dir.join("mnt");
assert!(fs::create_dir(&mnt_path).is_ok());
- start_fuse(zip_path, &mnt_path);
+ let noexec = false;
+ start_fuse(zip_path, &mnt_path, noexec);
// Give some time for the fuse to boot up
assert!(wait_for_mount(&mnt_path).is_ok());