Merge "Skip AVF tests if GSI && VSR < 202404" into main
diff --git a/demo/java/com/android/microdroid/demo/MainActivity.java b/demo/java/com/android/microdroid/demo/MainActivity.java
index f27b23b..906d18e 100644
--- a/demo/java/com/android/microdroid/demo/MainActivity.java
+++ b/demo/java/com/android/microdroid/demo/MainActivity.java
@@ -76,8 +76,10 @@
                         model.stop();
                     } else {
                         CheckBox debugModeCheckBox = findViewById(R.id.debugMode);
+                        CheckBox protectedModeCheckBox = findViewById(R.id.protectedMode);
                         final boolean debug = debugModeCheckBox.isChecked();
-                        model.run(debug);
+                        final boolean protectedVm = protectedModeCheckBox.isChecked();
+                        model.run(debug, protectedVm);
                     }
                 });
 
@@ -157,7 +159,7 @@
         }
 
         /** Runs a VM */
-        public void run(boolean debug) {
+        public void run(boolean debug, boolean protectedVm) {
             // Create a VM and run it.
             mExecutorService = Executors.newFixedThreadPool(4);
 
@@ -243,7 +245,8 @@
                 VirtualMachineConfig.Builder builder =
                         new VirtualMachineConfig.Builder(getApplication());
                 builder.setPayloadBinaryName("MicrodroidTestNativeLib.so");
-                builder.setProtectedVm(true);
+                builder.setProtectedVm(protectedVm);
+
                 if (debug) {
                     builder.setDebugLevel(VirtualMachineConfig.DEBUG_LEVEL_FULL);
                     builder.setVmOutputCaptured(true);
diff --git a/demo/res/layout/activity_main.xml b/demo/res/layout/activity_main.xml
index f0e35d6..baa7b1f 100644
--- a/demo/res/layout/activity_main.xml
+++ b/demo/res/layout/activity_main.xml
@@ -31,6 +31,13 @@
                 android:layout_height="wrap_content"
                 android:layout_weight="1"
                 android:text="Debug mode" />
+
+            <CheckBox
+                android:id="@+id/protectedMode"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="Protected vm" />
         </LinearLayout>
 
         <TextView
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java b/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
index a8f318c..1b915cd 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -601,7 +601,7 @@
         config.name = Optional.ofNullable(customImageConfig.getName()).orElse("");
         config.instanceId = new byte[64];
         config.kernel =
-                Optional.of(customImageConfig.getKernelPath())
+                Optional.ofNullable(customImageConfig.getKernelPath())
                         .map(
                                 (path) -> {
                                     try {
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java b/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
index 8d294fd..8ec9d2c 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
@@ -16,7 +16,6 @@
 
 package android.system.virtualmachine;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.PersistableBundle;
 
@@ -38,7 +37,7 @@
     private static final String KEY_KEYBOARD = "keyboard";
 
     @Nullable private final String name;
-    @NonNull private final String kernelPath;
+    @Nullable private final String kernelPath;
     @Nullable private final String initrdPath;
     @Nullable private final String bootloaderPath;
     @Nullable private final String[] params;
@@ -62,7 +61,7 @@
         return initrdPath;
     }
 
-    @NonNull
+    @Nullable
     public String getKernelPath() {
         return kernelPath;
     }
diff --git a/libs/bssl/error/src/lib.rs b/libs/bssl/error/src/lib.rs
index 82a2d5e..0bdb821 100644
--- a/libs/bssl/error/src/lib.rs
+++ b/libs/bssl/error/src/lib.rs
@@ -88,6 +88,8 @@
     EC_KEY_new_by_curve_name,
     EC_KEY_set_public_key_affine_coordinates,
     EC_POINT_get_affine_coordinates,
+    ECDSA_SIG_new,
+    ECDSA_SIG_set0,
     ECDSA_sign,
     ECDSA_size,
     ECDSA_verify,
@@ -105,6 +107,7 @@
     EVP_DigestVerifyInit,
     HKDF,
     HMAC,
+    i2d_ECDSA_SIG,
     RAND_bytes,
     SHA256,
 }
diff --git a/libs/bssl/src/ec_key.rs b/libs/bssl/src/ec_key.rs
index 897f8a1..9eb5e5c 100644
--- a/libs/bssl/src/ec_key.rs
+++ b/libs/bssl/src/ec_key.rs
@@ -22,15 +22,17 @@
 use alloc::vec::Vec;
 use bssl_avf_error::{ApiName, Error, Result};
 use bssl_sys::{
-    BN_bin2bn, BN_bn2bin_padded, BN_clear_free, BN_new, CBB_flush, CBB_len, ECDSA_sign, ECDSA_size,
-    ECDSA_verify, EC_GROUP_get_curve_name, EC_GROUP_new_by_curve_name, EC_KEY_check_key,
-    EC_KEY_free, EC_KEY_generate_key, EC_KEY_get0_group, EC_KEY_get0_public_key,
-    EC_KEY_marshal_private_key, EC_KEY_new_by_curve_name, EC_KEY_parse_private_key,
-    EC_KEY_set_public_key_affine_coordinates, EC_POINT_get_affine_coordinates,
-    NID_X9_62_prime256v1, NID_secp384r1, BIGNUM, EC_GROUP, EC_KEY, EC_POINT,
+    i2d_ECDSA_SIG, BN_bin2bn, BN_bn2bin_padded, BN_clear_free, BN_new, CBB_flush, CBB_len,
+    ECDSA_SIG_free, ECDSA_SIG_new, ECDSA_SIG_set0, ECDSA_sign, ECDSA_size, ECDSA_verify,
+    EC_GROUP_get_curve_name, EC_GROUP_new_by_curve_name, EC_KEY_check_key, EC_KEY_free,
+    EC_KEY_generate_key, EC_KEY_get0_group, EC_KEY_get0_public_key, EC_KEY_marshal_private_key,
+    EC_KEY_new_by_curve_name, EC_KEY_parse_private_key, EC_KEY_set_public_key_affine_coordinates,
+    EC_POINT_get_affine_coordinates, NID_X9_62_prime256v1, NID_secp384r1, BIGNUM, ECDSA_SIG,
+    EC_GROUP, EC_KEY, EC_POINT,
 };
 use cbor_util::{get_label_value, get_label_value_as_bytes};
 use ciborium::Value;
+use core::mem;
 use core::ptr::{self, NonNull};
 use coset::{
     iana::{self, EnumI64},
@@ -144,7 +146,7 @@
     /// Verifies the DER-encoded ECDSA `signature` of the `digest` with the current `EcKey`.
     ///
     /// Returns Ok(()) if the verification succeeds, otherwise an error will be returned.
-    pub fn ecdsa_verify(&self, signature: &[u8], digest: &[u8]) -> Result<()> {
+    pub fn ecdsa_verify_der(&self, signature: &[u8], digest: &[u8]) -> Result<()> {
         // The `type` argument should be 0 as required in the BoringSSL spec.
         const TYPE: i32 = 0;
 
@@ -163,6 +165,15 @@
         check_int_result(ret, ApiName::ECDSA_verify)
     }
 
+    /// Verifies the NIST-encoded (R | S) ECDSA `signature` of the `digest` with the current
+    /// `EcKey`.
+    ///
+    /// Returns Ok(()) if the verification succeeds, otherwise an error will be returned.
+    pub fn ecdsa_verify_nist(&self, signature: &[u8], digest: &[u8]) -> Result<()> {
+        let signature = ec_cose_signature_to_der(signature)?;
+        self.ecdsa_verify_der(&signature, digest)
+    }
+
     /// Signs the `digest` with the current `EcKey` using ECDSA.
     ///
     /// Returns the DER-encoded ECDSA signature.
@@ -324,6 +335,85 @@
     }
 }
 
+/// Convert a R | S format NIST signature to a DER-encoded form.
+fn ec_cose_signature_to_der(signature: &[u8]) -> Result<Vec<u8>> {
+    let mut ec_sig = EcSignature::new()?;
+    ec_sig.load_from_nist(signature)?;
+    ec_sig.to_der()
+}
+
+/// Wrapper for an `ECDSA_SIG` object representing an EC signature.
+struct EcSignature(NonNull<ECDSA_SIG>);
+
+impl EcSignature {
+    /// Allocate a signature object.
+    fn new() -> Result<Self> {
+        // SAFETY: We take ownership of the returned pointer if it is non-null.
+        let signature = unsafe { ECDSA_SIG_new() };
+
+        let signature =
+            NonNull::new(signature).ok_or_else(|| to_call_failed_error(ApiName::ECDSA_SIG_new))?;
+        Ok(Self(signature))
+    }
+
+    /// Populate the signature parameters from a NIST encoding (R | S).
+    fn load_from_nist(&mut self, signature: &[u8]) -> Result<()> {
+        let coord_bytes = signature.len() / 2;
+        if signature.len() != 2 * coord_bytes {
+            return Err(Error::InternalError);
+        }
+        let mut r = BigNum::from_slice(&signature[..coord_bytes])?;
+        let mut s = BigNum::from_slice(&signature[coord_bytes..])?;
+
+        check_int_result(
+            // SAFETY: The ECDSA_SIG was properly allocated and not yet freed. We have ownership
+            // of the two BigNums and they are not null.
+            unsafe { ECDSA_SIG_set0(self.0.as_mut(), r.as_mut_ptr(), s.as_mut_ptr()) },
+            ApiName::ECDSA_SIG_set0,
+        )?;
+
+        // On success, the ECDSA_SIG has taken ownership of the BigNums.
+        mem::forget(r);
+        mem::forget(s);
+
+        Ok(())
+    }
+
+    /// Return the signature encoded as DER.
+    fn to_der(&self) -> Result<Vec<u8>> {
+        // SAFETY: The ECDSA_SIG was properly allocated and not yet freed. Null is a valid
+        // value for `outp`; no output is written.
+        let len = unsafe { i2d_ECDSA_SIG(self.0.as_ptr(), ptr::null_mut()) };
+        if len < 0 {
+            return Err(to_call_failed_error(ApiName::i2d_ECDSA_SIG));
+        }
+
+        let mut buf = vec![0; len.try_into().map_err(|_| Error::InternalError)?];
+        let outp = &mut buf.as_mut_ptr();
+        // SAFETY: The ECDSA_SIG was properly allocated and not yet freed. `outp` is a non-null
+        // pointer to a mutable buffer of the right size to which the result will be written.
+        let final_len = unsafe { i2d_ECDSA_SIG(self.0.as_ptr(), outp) };
+        if final_len < 0 {
+            return Err(to_call_failed_error(ApiName::i2d_ECDSA_SIG));
+        }
+        // The input hasn't changed, so the length of the output shouldn't have. If it has we
+        // already have potentially undefined behavior so panic.
+        assert_eq!(
+            len, final_len,
+            "i2d_ECDSA_SIG returned inconsistent lengths: {len}, {final_len}"
+        );
+
+        Ok(buf)
+    }
+}
+
+impl Drop for EcSignature {
+    fn drop(&mut self) {
+        // SAFETY: The pointer was allocated by `ECDSA_SIG_new`.
+        unsafe { ECDSA_SIG_free(self.0.as_mut()) };
+    }
+}
+
 /// Wrapper of an `EC_GROUP` reference.
 struct EcGroup<'a>(&'a EC_GROUP);
 
diff --git a/libs/bssl/tests/eckey_test.rs b/libs/bssl/tests/eckey_test.rs
index 3c0e45d..00ed6c5 100644
--- a/libs/bssl/tests/eckey_test.rs
+++ b/libs/bssl/tests/eckey_test.rs
@@ -88,7 +88,7 @@
     assert_eq!(digest, sha256(MESSAGE1)?);
 
     let signature = ec_key.ecdsa_sign(&digest)?;
-    ec_key.ecdsa_verify(&signature, &digest)?;
+    ec_key.ecdsa_verify_der(&signature, &digest)?;
     // Building a `PKey` from a temporary `CoseKey` should work as the lifetime
     // of the `PKey` is not tied to the lifetime of the `CoseKey`.
     let pkey = PKey::from_cose_public_key(&ec_key.cose_public_key()?)?;
@@ -103,7 +103,7 @@
     let digest = digester.digest(MESSAGE1)?;
 
     let signature = ec_key.ecdsa_sign(&digest)?;
-    ec_key.ecdsa_verify(&signature, &digest)?;
+    ec_key.ecdsa_verify_der(&signature, &digest)?;
     let pkey = PKey::from_cose_public_key(&ec_key.cose_public_key()?)?;
     pkey.verify(&signature, MESSAGE1, Some(digester))
 }
@@ -117,7 +117,7 @@
 
     let mut ec_key2 = EcKey::new_p256()?;
     ec_key2.generate_key()?;
-    let err = ec_key2.ecdsa_verify(&signature, &digest).unwrap_err();
+    let err = ec_key2.ecdsa_verify_der(&signature, &digest).unwrap_err();
     let expected_err = Error::CallFailed(ApiName::ECDSA_verify, EcdsaError::BadSignature.into());
     assert_eq!(expected_err, err);
 
@@ -137,7 +137,7 @@
     let signature = ec_key.ecdsa_sign(&digest1)?;
     let digest2 = sha256(MESSAGE2)?;
 
-    let err = ec_key.ecdsa_verify(&signature, &digest2).unwrap_err();
+    let err = ec_key.ecdsa_verify_der(&signature, &digest2).unwrap_err();
     let expected_err = Error::CallFailed(ApiName::ECDSA_verify, EcdsaError::BadSignature.into());
     assert_eq!(expected_err, err);
     Ok(())
diff --git a/libs/libfdt/src/libfdt.rs b/libs/libfdt/src/libfdt.rs
index 1af9edf..9ddfbaa 100644
--- a/libs/libfdt/src/libfdt.rs
+++ b/libs/libfdt/src/libfdt.rs
@@ -311,6 +311,7 @@
     }
 
     /// Safe wrapper around `fdt_open_into()` (C function).
+    #[allow(dead_code)]
     fn open_into(&self, dest: &mut [u8]) -> Result<()> {
         let fdt = self.as_fdt_slice().as_ptr().cast();
 
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index 9151ce1..0d57301 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -228,7 +228,7 @@
     let tbs_cert = cert.tbs_certificate;
     let digest = sha256(&tbs_cert.to_der().unwrap()).unwrap();
     authority_public_key
-        .ecdsa_verify(cert.signature.raw_bytes(), &digest)
+        .ecdsa_verify_der(cert.signature.raw_bytes(), &digest)
         .expect("Failed to verify the certificate signature with the authority public key");
 
     // Checks that the certificate's subject public key is equal to the key in the CSR.
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
index d2e674b..00effd7 100644
--- a/service_vm/requests/src/client_vm.rs
+++ b/service_vm/requests/src/client_vm.rs
@@ -68,6 +68,7 @@
     cose_sign.verify_signature(ATTESTATION_KEY_SIGNATURE_INDEX, aad, |signature, message| {
         ecdsa_verify(&ec_public_key, signature, message)
     })?;
+
     let subject_public_key_info = PKey::try_from(ec_public_key)?.subject_public_key_info()?;
 
     // Builds the TBSCertificate.
@@ -80,12 +81,9 @@
     rand_bytes(&mut serial_number)?;
     let subject = Name::encode_from_string("CN=Android Protected Virtual Machine Key")?;
     let rkp_cert = Certificate::from_der(&params.remotely_provisioned_cert)?;
+    let vm_components = client_vm_dice_chain.microdroid_payload_components()?;
     let vm_components =
-        if let Some(components) = client_vm_dice_chain.microdroid_payload_components() {
-            components.iter().map(cert::VmComponent::new).collect::<der::Result<Vec<_>>>()?
-        } else {
-            Vec::new()
-        };
+        vm_components.iter().map(cert::VmComponent::new).collect::<der::Result<Vec<_>>>()?;
 
     info!("The client VM DICE chain validation succeeded. Beginning to generate the certificate.");
     let attestation_ext = cert::AttestationExtension::new(
@@ -120,7 +118,7 @@
 fn ecdsa_verify(key: &EcKey, signature: &[u8], message: &[u8]) -> bssl_avf::Result<()> {
     // The message was signed with ECDSA with curve P-256 and SHA-256 at the signature generation.
     let digest = sha256(message)?;
-    key.ecdsa_verify(signature, &digest)
+    key.ecdsa_verify_der(signature, &digest)
 }
 
 fn ecdsa_sign(key: &EcKey, message: &[u8]) -> bssl_avf::Result<Vec<u8>> {
diff --git a/service_vm/requests/src/dice.rs b/service_vm/requests/src/dice.rs
index df29676..f4da7ef 100644
--- a/service_vm/requests/src/dice.rs
+++ b/service_vm/requests/src/dice.rs
@@ -15,6 +15,7 @@
 //! This module contains functions related to DICE.
 
 use alloc::string::String;
+use alloc::vec;
 use alloc::vec::Vec;
 use bssl_avf::{ed25519_verify, Digester, EcKey};
 use cbor_util::{
@@ -112,11 +113,10 @@
     ) -> Result<Self> {
         let microdroid_payload_name =
             &dice_entry_payloads[dice_entry_payloads.len() - 1].config_descriptor.component_name;
-        if MICRODROID_PAYLOAD_COMPONENT_NAME != microdroid_payload_name {
+        if Some(MICRODROID_PAYLOAD_COMPONENT_NAME) != microdroid_payload_name.as_deref() {
             error!(
                 "The last entry in the client VM DICE chain must describe the Microdroid \
-                 payload. Got '{}'",
-                microdroid_payload_name
+                 payload. Got '{microdroid_payload_name:?}'"
             );
             return Err(RequestProcessingError::InvalidDiceChain);
         }
@@ -125,11 +125,10 @@
             let index = dice_entry_payloads.len() - 2;
             let vendor_partition_name =
                 &dice_entry_payloads[index].config_descriptor.component_name;
-            if VENDOR_PARTITION_COMPONENT_NAME != vendor_partition_name {
+            if Some(VENDOR_PARTITION_COMPONENT_NAME) != vendor_partition_name.as_deref() {
                 error!(
                     "The vendor partition entry in the client VM DICE chain must describe the \
-                        vendor partition. Got '{}'",
-                    vendor_partition_name,
+                    vendor partition. Got '{vendor_partition_name:?}'"
                 );
                 return Err(RequestProcessingError::InvalidDiceChain);
             }
@@ -139,11 +138,10 @@
         };
 
         let kernel_name = &dice_entry_payloads[kernel_index].config_descriptor.component_name;
-        if KERNEL_COMPONENT_NAME != kernel_name {
+        if Some(KERNEL_COMPONENT_NAME) != kernel_name.as_deref() {
             error!(
                 "The microdroid kernel entry in the client VM DICE chain must describe the \
-                 Microdroid kernel. Got '{}'",
-                kernel_name,
+                 Microdroid kernel. Got '{kernel_name:?}'"
             );
             return Err(RequestProcessingError::InvalidDiceChain);
         }
@@ -164,8 +162,8 @@
         &self.payloads[self.payloads.len() - 1]
     }
 
-    pub(crate) fn microdroid_payload_components(&self) -> Option<&Vec<SubComponent>> {
-        self.microdroid_payload().config_descriptor.sub_components.as_ref()
+    pub(crate) fn microdroid_payload_components(&self) -> Result<Vec<SubComponent>> {
+        self.microdroid_payload().config_descriptor.sub_components()
     }
 
     /// Returns true if all payloads in the DICE chain are in normal mode.
@@ -210,7 +208,12 @@
     type Error = RequestProcessingError;
 
     fn try_from(key: CoseKey) -> Result<Self> {
-        if !key.key_ops.contains(&KeyOperation::Assigned(iana::KeyOperation::Verify)) {
+        // The public key must allow use for verification.
+        // Note that an empty key_ops set implicitly allows everything.
+        let key_ops = &key.key_ops;
+        if !key_ops.is_empty()
+            && !key_ops.contains(&KeyOperation::Assigned(iana::KeyOperation::Verify))
+        {
             error!("Public key does not support verification");
             return Err(RequestProcessingError::InvalidDiceChain);
         }
@@ -226,6 +229,9 @@
     /// generateCertificateRequestV2.cddl:
     ///
     /// PubKeyEd25519 / PubKeyECDSA256 / PubKeyECDSA384
+    ///
+    /// The signature should be in the format defined by COSE in RFC 9053 section 2 for the
+    /// specifric algorithm.
     pub(crate) fn verify(&self, signature: &[u8], message: &[u8]) -> Result<()> {
         match &self.0.kty {
             KeyType::Assigned(iana::KeyType::EC2) => {
@@ -243,7 +249,7 @@
                     }
                 };
                 let digest = digester.digest(message)?;
-                Ok(public_key.ecdsa_verify(signature, &digest)?)
+                Ok(public_key.ecdsa_verify_nist(signature, &digest)?)
             }
             KeyType::Assigned(iana::KeyType::OKP) => {
                 let curve_type =
@@ -346,15 +352,21 @@
 ///
 /// hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
 /// generateCertificateRequestV2.cddl
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Default)]
 pub(crate) struct ConfigDescriptor {
-    component_name: String,
-    sub_components: Option<Vec<SubComponent>>,
+    component_name: Option<String>,
+    sub_components: Option<Value>,
 }
 
 impl ConfigDescriptor {
     fn from_slice(data: &[u8]) -> Result<Self> {
-        let value = Value::from_slice(data)?;
+        let value = Value::from_slice(data);
+        let Ok(value) = value else {
+            // Some DICE implementations store a hash in the config descriptor. So we just
+            // skip anything that doesn't parse correctly.
+            info!("Ignoring malformed config descriptor");
+            return Ok(Default::default());
+        };
         let entries = value_to_map(value, "ConfigDescriptor")?;
         let mut builder = ConfigDescriptorBuilder::default();
         for (key, value) in entries.into_iter() {
@@ -365,24 +377,31 @@
                     builder.component_name(name)?;
                 }
                 CONFIG_DESC_SUB_COMPONENTS => {
-                    let sub_components = value_to_array(value, "ConfigDescriptor sub_components")?;
-                    let sub_components = sub_components
-                        .into_iter()
-                        .map(SubComponent::try_from)
-                        .collect::<Result<Vec<_>>>()?;
-                    builder.sub_components(sub_components)?
+                    // If this is the Microdroid payload node then these are the subcomponents. But
+                    // for any other node it could be anything - this isn't a reserved key. So defer
+                    // decoding until we know which node is which.
+                    builder.sub_components(value)?
                 }
                 _ => {}
             }
         }
         builder.build()
     }
+
+    /// Attempt to decode any Microdroid sub-components that were present in this config descriptor.
+    fn sub_components(&self) -> Result<Vec<SubComponent>> {
+        let Some(value) = &self.sub_components else {
+            return Ok(vec![]);
+        };
+        let sub_components = value_to_array(value.clone(), "ConfigDescriptor sub_components")?;
+        sub_components.into_iter().map(SubComponent::try_from).collect()
+    }
 }
 
 #[derive(Debug, Clone, Default)]
 struct ConfigDescriptorBuilder {
     component_name: OnceCell<String>,
-    sub_components: OnceCell<Vec<SubComponent>>,
+    sub_components: OnceCell<Value>,
 }
 
 impl ConfigDescriptorBuilder {
@@ -390,13 +409,12 @@
         set_once(&self.component_name, component_name, "ConfigDescriptor component_name")
     }
 
-    fn sub_components(&mut self, sub_components: Vec<SubComponent>) -> Result<()> {
+    fn sub_components(&mut self, sub_components: Value) -> Result<()> {
         set_once(&self.sub_components, sub_components, "ConfigDescriptor sub_components")
     }
 
     fn build(mut self) -> Result<ConfigDescriptor> {
-        let component_name =
-            take_value(&mut self.component_name, "ConfigDescriptor component_name")?;
+        let component_name = self.component_name.take();
         let sub_components = self.sub_components.take();
         Ok(ConfigDescriptor { component_name, sub_components })
     }
diff --git a/service_vm/test_apk/Android.bp b/service_vm/test_apk/Android.bp
index 72e411e..1ba156f 100644
--- a/service_vm/test_apk/Android.bp
+++ b/service_vm/test_apk/Android.bp
@@ -6,6 +6,7 @@
     name: "vm_attestation_testapp_defaults",
     test_suites: [
         "general-tests",
+        "pts",
     ],
     static_libs: [
         "MicrodroidDeviceTestHelper",
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index f939678..f1509e2 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -908,14 +908,14 @@
             append_kernel_param("androidboot.microdroid.mount_vendor=1", &mut vm_config)
         }
 
-        vm_config.devices = custom_config.devices.clone();
+        vm_config.devices.clone_from(&custom_config.devices);
     }
 
     if config.memoryMib > 0 {
         vm_config.memoryMib = config.memoryMib;
     }
 
-    vm_config.name = config.name.clone();
+    vm_config.name.clone_from(&config.name);
     vm_config.protectedVm = config.protectedVm;
     vm_config.cpuTopology = config.cpuTopology;
 
diff --git a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
index 4c42bb4..ec0f8e8 100644
--- a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
+++ b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
@@ -91,7 +91,7 @@
                         .forEach(customImageConfigBuilder::addParam);
             }
             if (json.has("bootloader")) {
-                customImageConfigBuilder.setInitrdPath(json.getString("bootloader"));
+                customImageConfigBuilder.setBootloaderPath(json.getString("bootloader"));
             }
             if (json.has("disks")) {
                 JSONArray diskArr = json.getJSONArray("disks");