Merge "Importing rustc-1.66.0"
diff --git a/authfs/testdata/README.md b/authfs/testdata/README.md
index cf641a9..2df6753 100644
--- a/authfs/testdata/README.md
+++ b/authfs/testdata/README.md
@@ -2,7 +2,7 @@
=================
With a key pair, fs-verity signature can be generated by simply running
`fsverity_metadata_generator` command line tool, which uses
-[fsverity-util](https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/fsverity-utils.git).
+[fsverity-utils](https://git.kernel.org/pub/scm/fs/fsverity/fsverity-utils.git)
```
fsverity_metadata_generator --fsverity-path {fsverity_path} --key key.pem --key-format pem \
diff --git a/javalib/api/system-current.txt b/javalib/api/system-current.txt
index 1977321..fe9943d 100644
--- a/javalib/api/system-current.txt
+++ b/javalib/api/system-current.txt
@@ -56,7 +56,7 @@
}
public final class VirtualMachineConfig {
- method @NonNull public String getApkPath();
+ method @Nullable public String getApkPath();
method @NonNull public int getDebugLevel();
method @IntRange(from=0) public long getEncryptedStorageKib();
method @IntRange(from=0) public int getMemoryMib();
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index e66cf29..7c7f4b5 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -779,7 +779,8 @@
createVmPipes();
}
- VirtualMachineAppConfig appConfig = getConfig().toVsConfig();
+ VirtualMachineAppConfig appConfig =
+ getConfig().toVsConfig(mContext.getPackageManager());
appConfig.name = mName;
try {
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index 1fd49c8..b358f9e 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -29,6 +29,8 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.sysprop.HypervisorProperties;
@@ -58,8 +60,9 @@
private static final String[] EMPTY_STRING_ARRAY = {};
// These define the schema of the config file persisted on disk.
- private static final int VERSION = 3;
+ private static final int VERSION = 4;
private static final String KEY_VERSION = "version";
+ private static final String KEY_PACKAGENAME = "packageName";
private static final String KEY_APKPATH = "apkPath";
private static final String KEY_PAYLOADCONFIGPATH = "payloadConfigPath";
private static final String KEY_PAYLOADBINARYNAME = "payloadBinaryPath";
@@ -94,8 +97,11 @@
*/
@SystemApi public static final int DEBUG_LEVEL_FULL = 1;
+ /** Name of a package whose primary APK contains the VM payload. */
+ @Nullable private final String mPackageName;
+
/** Absolute path to the APK file containing the VM payload. */
- @NonNull private final String mApkPath;
+ @Nullable private final String mApkPath;
@DebugLevel private final int mDebugLevel;
@@ -129,7 +135,8 @@
private final boolean mVmOutputCaptured;
private VirtualMachineConfig(
- @NonNull String apkPath,
+ @Nullable String packageName,
+ @Nullable String apkPath,
@Nullable String payloadConfigPath,
@Nullable String payloadBinaryName,
@DebugLevel int debugLevel,
@@ -139,6 +146,7 @@
long encryptedStorageKib,
boolean vmOutputCaptured) {
// This is only called from Builder.build(); the builder handles parameter validation.
+ mPackageName = packageName;
mApkPath = apkPath;
mPayloadConfigPath = payloadConfigPath;
mPayloadBinaryName = payloadBinaryName;
@@ -191,8 +199,13 @@
"Version " + version + " too high; current is " + VERSION);
}
- Builder builder = new Builder();
- builder.setApkPath(b.getString(KEY_APKPATH));
+ String packageName = b.getString(KEY_PACKAGENAME);
+ Builder builder = new Builder(packageName);
+
+ String apkPath = b.getString(KEY_APKPATH);
+ if (apkPath != null) {
+ builder.setApkPath(apkPath);
+ }
String payloadConfigPath = b.getString(KEY_PAYLOADCONFIGPATH);
if (payloadConfigPath == null) {
@@ -234,7 +247,12 @@
private void serializeOutputStream(@NonNull OutputStream output) throws IOException {
PersistableBundle b = new PersistableBundle();
b.putInt(KEY_VERSION, VERSION);
- b.putString(KEY_APKPATH, mApkPath);
+ if (mPackageName != null) {
+ b.putString(KEY_PACKAGENAME, mPackageName);
+ }
+ if (mApkPath != null) {
+ b.putString(KEY_APKPATH, mApkPath);
+ }
b.putString(KEY_PAYLOADCONFIGPATH, mPayloadConfigPath);
b.putString(KEY_PAYLOADBINARYNAME, mPayloadBinaryName);
b.putInt(KEY_DEBUGLEVEL, mDebugLevel);
@@ -252,12 +270,13 @@
/**
* Returns the absolute path of the APK which should contain the binary payload that will
- * execute within the VM.
+ * execute within the VM. Returns null if no specific path has been set, so the primary APK will
+ * be used.
*
* @hide
*/
@SystemApi
- @NonNull
+ @Nullable
public String getApkPath() {
return mApkPath;
}
@@ -383,7 +402,8 @@
&& this.mVmOutputCaptured == other.mVmOutputCaptured
&& Objects.equals(this.mPayloadConfigPath, other.mPayloadConfigPath)
&& Objects.equals(this.mPayloadBinaryName, other.mPayloadBinaryName)
- && this.mApkPath.equals(other.mApkPath);
+ && Objects.equals(this.mPackageName, other.mPackageName)
+ && Objects.equals(this.mApkPath, other.mApkPath);
}
/**
@@ -393,11 +413,25 @@
* app-owned files and that could be abused to run a VM with software that the calling
* application doesn't own.
*/
- VirtualMachineAppConfig toVsConfig() throws VirtualMachineException {
+ VirtualMachineAppConfig toVsConfig(@NonNull PackageManager packageManager)
+ throws VirtualMachineException {
VirtualMachineAppConfig vsConfig = new VirtualMachineAppConfig();
+ String apkPath = mApkPath;
+ if (apkPath == null) {
+ try {
+ ApplicationInfo appInfo =
+ packageManager.getApplicationInfo(
+ mPackageName, PackageManager.ApplicationInfoFlags.of(0));
+ // This really is the path to the APK, not a directory.
+ apkPath = appInfo.sourceDir;
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new VirtualMachineException("Package not found", e);
+ }
+ }
+
try {
- vsConfig.apk = ParcelFileDescriptor.open(new File(mApkPath), MODE_READ_ONLY);
+ vsConfig.apk = ParcelFileDescriptor.open(new File(apkPath), MODE_READ_ONLY);
} catch (FileNotFoundException e) {
throw new VirtualMachineException("Failed to open APK", e);
}
@@ -433,7 +467,7 @@
*/
@SystemApi
public static final class Builder {
- @Nullable private final Context mContext;
+ @Nullable private final String mPackageName;
@Nullable private String mApkPath;
@Nullable private String mPayloadConfigPath;
@Nullable private String mPayloadBinaryName;
@@ -452,15 +486,15 @@
*/
@SystemApi
public Builder(@NonNull Context context) {
- mContext = requireNonNull(context, "context must not be null");
+ mPackageName = requireNonNull(context, "context must not be null").getPackageName();
}
/**
- * Creates a builder with no associated context; {@link #setApkPath} must be called to
- * specify which APK contains the payload.
+ * Creates a builder for a specific package. If packageName is null, {@link #setApkPath}
+ * must be called to specify the APK containing the payload.
*/
- private Builder() {
- mContext = null;
+ private Builder(@Nullable String packageName) {
+ mPackageName = packageName;
}
/**
@@ -471,14 +505,16 @@
@SystemApi
@NonNull
public VirtualMachineConfig build() {
- String apkPath;
- if (mApkPath == null) {
- if (mContext == null) {
- throw new IllegalStateException("apkPath must be specified");
- }
- apkPath = mContext.getPackageCodePath();
- } else {
+ String apkPath = null;
+ String packageName = null;
+
+ if (mApkPath != null) {
apkPath = mApkPath;
+ } else if (mPackageName != null) {
+ packageName = mPackageName;
+ } else {
+ // This should never happen, unless we're deserializing a bad config
+ throw new IllegalStateException("apkPath or packageName must be specified");
}
if (mPayloadBinaryName == null) {
@@ -501,6 +537,7 @@
}
return new VirtualMachineConfig(
+ packageName,
apkPath,
mPayloadConfigPath,
mPayloadBinaryName,
diff --git a/libs/fdtpci/src/lib.rs b/libs/fdtpci/src/lib.rs
index 1ddda9f..e32e16d 100644
--- a/libs/fdtpci/src/lib.rs
+++ b/libs/fdtpci/src/lib.rs
@@ -91,7 +91,7 @@
}
/// Information about the PCI bus parsed from the device tree.
-#[derive(Debug)]
+#[derive(Clone, Debug)]
pub struct PciInfo {
/// The MMIO range used by the memory-mapped PCI CAM.
pub cam_range: Range<usize>,
diff --git a/microdroid/init.rc b/microdroid/init.rc
index bc42791..ce0cab4 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -149,6 +149,10 @@
# Mark boot completed. This will notify microdroid_manager to run payload.
setprop dev.bootcomplete 1
+on property:tombstone_transmit.start=1
+ mkdir /data/tombstones 0771 system system
+ start tombstone_transmit
+
service tombstone_transmit /system/bin/tombstone_transmit.microdroid -cid 2 -port 2000 -remove_tombstones_after_transmitting
user system
group system
@@ -175,4 +179,3 @@
group shell log readproc
seclabel u:r:shell:s0
setenv HOSTNAME console
-
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 24a12f7..f1c41b9 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -426,7 +426,8 @@
// Start tombstone_transmit if enabled
if config.export_tombstones {
- control_service("start", "tombstone_transmit")?;
+ system_properties::write("tombstone_transmit.start", "1")
+ .context("set tombstone_transmit.start")?;
} else {
control_service("stop", "tombstoned")?;
}
@@ -436,10 +437,6 @@
register_vm_payload_service(allow_restricted_apis, service.clone(), dice_context)?;
- if config.export_tombstones {
- wait_for_tombstone_transmit_done()?;
- }
-
// 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
@@ -451,6 +448,12 @@
wait_for_property_true("dev.bootcomplete").context("failed waiting for dev.bootcomplete")?;
system_properties::write("microdroid_manager.init_done", "1")
.context("set microdroid_manager.init_done")?;
+
+ // Wait for tombstone_transmit to init
+ if config.export_tombstones {
+ wait_for_tombstone_transmit_done()?;
+ }
+
info!("boot completed, time to run payload");
exec_task(task, service).context("Failed to run payload")
}
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index f5e214e..21f84a5 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -18,6 +18,7 @@
"libfdtpci",
"liblibfdt",
"liblog_rust_nostd",
+ "libonce_cell_nostd",
"libpvmfw_avb_nostd",
"libpvmfw_embedded_key",
"libtinyvec_nostd",
diff --git a/pvmfw/README.md b/pvmfw/README.md
index f46c718..1e4b605 100644
--- a/pvmfw/README.md
+++ b/pvmfw/README.md
@@ -214,7 +214,9 @@
[AVB]: https://source.android.com/docs/security/features/verifiedboot/boot-flow
[BccHandover]: https://pigweed.googlesource.com/open-dice/+/825e3beb6c/src/android/bcc.c#260
+[BccHandoverMainFlow]: https://pigweed.googlesource.com/open-dice/+/825e3beb6c/src/android/bcc.c#199
[CDDL]: https://datatracker.ietf.org/doc/rfc8610
+[dice-mode]: https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/specification.md#Mode-Value-Details
[dice-dt]: https://www.kernel.org/doc/Documentation/devicetree/bindings/reserved-memory/google%2Copen-dice.yaml
[Layering]: https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/specification.md#layering-details
[Trusty-BCC]: https://android.googlesource.com/trusty/lib/+/1696be0a8f3a7103/lib/hwbcc/common/swbcc.c#554
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index a249e8d..24c36b3 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -45,7 +45,7 @@
helpers::flush,
helpers::GUEST_PAGE_SIZE,
memory::MemoryTracker,
- virtio::pci::{find_virtio_devices, map_mmio},
+ virtio::pci::{self, find_virtio_devices},
};
use ::dice::bcc;
use fdtpci::{PciError, PciInfo};
@@ -76,10 +76,7 @@
// Set up PCI bus for VirtIO devices.
let pci_info = PciInfo::from_fdt(fdt).map_err(handle_pci_error)?;
debug!("PCI: {:#x?}", pci_info);
- map_mmio(&pci_info, memory)?;
- // Safety: This is the only place where we call make_pci_root, and this main function is only
- // called once.
- let mut pci_root = unsafe { pci_info.make_pci_root() };
+ let mut pci_root = pci::initialise(pci_info, memory)?;
find_virtio_devices(&mut pci_root).map_err(handle_pci_error)?;
verify_payload(signed_kernel, ramdisk, PUBLIC_KEY).map_err(|e| {
diff --git a/pvmfw/src/virtio/hal.rs b/pvmfw/src/virtio/hal.rs
index c6c7a99..5f70b33 100644
--- a/pvmfw/src/virtio/hal.rs
+++ b/pvmfw/src/virtio/hal.rs
@@ -1,5 +1,9 @@
+use super::pci::PCI_INFO;
use crate::memory::{alloc_shared, dealloc_shared, phys_to_virt, virt_to_phys};
-use core::ptr::{copy_nonoverlapping, NonNull};
+use core::{
+ ops::Range,
+ ptr::{copy_nonoverlapping, NonNull},
+};
use log::debug;
use virtio_drivers::{BufferDirection, Hal, PhysAddr, PAGE_SIZE};
@@ -26,7 +30,21 @@
0
}
- fn mmio_phys_to_virt(paddr: PhysAddr, _size: usize) -> NonNull<u8> {
+ fn mmio_phys_to_virt(paddr: PhysAddr, size: usize) -> NonNull<u8> {
+ let pci_info = PCI_INFO.get().expect("VirtIO HAL used before PCI_INFO was initialised");
+ // Check that the region is within the PCI MMIO range that we read from the device tree. If
+ // not, the host is probably trying to do something malicious.
+ if !contains_range(
+ &pci_info.bar_range,
+ &(paddr.try_into().expect("PCI MMIO region start was outside of 32-bit address space")
+ ..paddr
+ .checked_add(size)
+ .expect("PCI MMIO region end overflowed")
+ .try_into()
+ .expect("PCI MMIO region end was outside of 32-bit address space")),
+ ) {
+ panic!("PCI MMIO region was outside of expected BAR range.");
+ }
phys_to_virt(paddr)
}
@@ -68,3 +86,8 @@
}
}
}
+
+/// Returns true if `inner` is entirely contained within `outer`.
+fn contains_range(outer: &Range<u32>, inner: &Range<u32>) -> bool {
+ inner.start >= outer.start && inner.end <= outer.end
+}
diff --git a/pvmfw/src/virtio/pci.rs b/pvmfw/src/virtio/pci.rs
index f9d36c6..d3b3124 100644
--- a/pvmfw/src/virtio/pci.rs
+++ b/pvmfw/src/virtio/pci.rs
@@ -16,8 +16,10 @@
use super::hal::HalImpl;
use crate::{entry::RebootReason, memory::MemoryTracker};
+use alloc::boxed::Box;
use fdtpci::{PciError, PciInfo};
use log::{debug, error, info};
+use once_cell::race::OnceBox;
use virtio_drivers::{
device::blk::VirtIOBlk,
transport::{
@@ -26,8 +28,29 @@
},
};
+pub(super) static PCI_INFO: OnceBox<PciInfo> = OnceBox::new();
+
+/// Prepares to use VirtIO PCI devices.
+///
+/// In particular:
+///
+/// 1. Maps the PCI CAM and BAR range in the page table and MMIO guard.
+/// 2. Stores the `PciInfo` for the VirtIO HAL to use later.
+/// 3. Creates and returns a `PciRoot`.
+///
+/// This must only be called once; it will panic if it is called a second time.
+pub fn initialise(pci_info: PciInfo, memory: &mut MemoryTracker) -> Result<PciRoot, RebootReason> {
+ map_mmio(&pci_info, memory)?;
+
+ PCI_INFO.set(Box::new(pci_info.clone())).expect("Tried to set PCI_INFO a second time");
+
+ // Safety: This is the only place where we call make_pci_root, and `PCI_INFO.set` above will
+ // panic if it is called a second time.
+ Ok(unsafe { pci_info.make_pci_root() })
+}
+
/// Maps the CAM and BAR range in the page table and MMIO guard.
-pub fn map_mmio(pci_info: &PciInfo, memory: &mut MemoryTracker) -> Result<(), RebootReason> {
+fn map_mmio(pci_info: &PciInfo, memory: &mut MemoryTracker) -> Result<(), RebootReason> {
memory.map_mmio_range(pci_info.cam_range.clone()).map_err(|e| {
error!("Failed to map PCI CAM: {}", e);
RebootReason::InternalError
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 e1a2e40..7bd5f08 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -31,6 +31,7 @@
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import android.content.Context;
+import android.content.ContextWrapper;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
@@ -342,7 +343,7 @@
VirtualMachineConfig.Builder minimalBuilder = newVmConfigBuilder();
VirtualMachineConfig minimal = minimalBuilder.setPayloadBinaryName("binary.so").build();
- assertThat(minimal.getApkPath()).isEqualTo(getContext().getPackageCodePath());
+ assertThat(minimal.getApkPath()).isNull();
assertThat(minimal.getDebugLevel()).isEqualTo(DEBUG_LEVEL_NONE);
assertThat(minimal.getMemoryMib()).isEqualTo(0);
assertThat(minimal.getNumCpus()).isEqualTo(1);
@@ -425,13 +426,9 @@
assertThat(e).hasMessageThat().contains("debug level must be FULL to capture output");
}
- private VirtualMachineConfig.Builder newBaselineBuilder() {
- return newVmConfigBuilder().setPayloadBinaryName("binary.so").setApkPath("/apk/path");
- }
-
@Test
@CddTest(requirements = {"9.17/C-1-1"})
- public void compatibleConfigTests() throws Exception {
+ public void compatibleConfigTests() {
int maxCpus = Runtime.getRuntime().availableProcessors();
VirtualMachineConfig baseline = newBaselineBuilder().build();
@@ -467,6 +464,31 @@
newBaselineBuilder().setDebugLevel(DEBUG_LEVEL_FULL);
VirtualMachineConfig debuggable = debuggableBuilder.build();
assertConfigCompatible(debuggable, debuggableBuilder.setVmOutputCaptured(true)).isFalse();
+
+ VirtualMachineConfig currentContextConfig =
+ new VirtualMachineConfig.Builder(getContext())
+ .setProtectedVm(isProtectedVm())
+ .setPayloadBinaryName("binary.so")
+ .build();
+
+ // packageName is not directly exposed by the config, so we have to be a bit creative
+ // to modify it.
+ Context otherContext =
+ new ContextWrapper(getContext()) {
+ @Override
+ public String getPackageName() {
+ return "other.package.name";
+ }
+ };
+ VirtualMachineConfig.Builder otherContextBuilder =
+ new VirtualMachineConfig.Builder(otherContext)
+ .setProtectedVm(isProtectedVm())
+ .setPayloadBinaryName("binary.so");
+ assertConfigCompatible(currentContextConfig, otherContextBuilder).isFalse();
+ }
+
+ private VirtualMachineConfig.Builder newBaselineBuilder() {
+ return newVmConfigBuilder().setPayloadBinaryName("binary.so").setApkPath("/apk/path");
}
private BooleanSubject assertConfigCompatible(